Define a fixed list of origins that are permitted to make cross-origin requests to your API. For each incoming request, check the Origin header against that list. If the origin matches, respond with that specific origin in Access-Control-Allow-Origin. If it doesn’t match, omit the CORS headers entirely — the browser will block the cross-origin access.
• One frontend: Access-Control-Allow-Origin: https://app.yourcompany.com (static, hardcoded)
• Multiple frontends: Check the Origin header against an allowlist: [“https://app.yourcompany.com”, “https://admin.yourcompany.com”]. Respond with the matched origin — not a wildcard, not a reflection.
• Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true — browsers reject this combination, but it indicates confused CORS thinking
• Reflecting the Origin header directly: response.setHeader(’Access-Control-Allow-Origin’, request.getHeader(’Origin’)) — this allows every origin, including attacker-controlled ones
• Trusting Origin: null — sandbox iframes and attacker-controlled redirects can send a null origin
• Substring matching: allowing any origin that contains “yourcompany.com” — attacker registers “evil-yourcompany.com” and bypasses the check
If your API is genuinely public and doesn’t use cookie-based auth, Access-Control-Allow-Origin: * without credentials is safe — the wildcard only grants access when credentials are not included. Public data APIs (weather, maps) commonly use this.
Ask your team what origins your API allows via CORS — if the answer is “all origins” or “whatever the caller says,” any website on the internet can make authenticated requests to your API on behalf of your logged-in users.