Safeguard
Developer Tools•

CORS with cookies: why the wildcard origin stops working

When a cross-origin request includes cookies, Access-Control-Allow-Origin cannot be a wildcard. Here are the exact rules and how to set them safely.

Safeguard Security Team
3 min read

When a cross-origin request carries credentials such as cookies, the server must name one exact origin in Access-Control-Allow-Origin, send Access-Control-Allow-Credentials: true, and avoid the * wildcard in the related allow headers. If the wildcard appears, the browser blocks the response and reports a CORS error.

Two switches, both required

Credentials are off by default for cross-origin calls. The client opts in: credentials: "include" for fetch, or withCredentials = true for XMLHttpRequest. The server then has to opt in too. MDN states the browser rejects a response that lacks Access-Control-Allow-Credentials: true.

If either side is missing, the request may still go out, but your script cannot read the response, and a Set-Cookie in it will not be stored.

What cannot be a wildcard

For a credentialed request, MDN says the server must not use * in:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Headers
  • Access-Control-Allow-Methods
  • Access-Control-Expose-Headers

Each needs an explicit value or list. The preflight is part of this: it never includes credentials itself, but its response must still carry Access-Control-Allow-Credentials: true.

Supporting more than one origin

The header holds a single origin. To serve several, compare the incoming Origin header against an allowlist you maintain, and echo it back only on a match. Also add Vary: Origin, so shared caches do not hand one origin's answer to another.

if request origin is in the allowlist:
    Access-Control-Allow-Origin: that exact origin
    Access-Control-Allow-Credentials: true
    Vary: Origin
else:
    send no CORS headers

Match on the full origin string, scheme and port included. Substring or suffix checks are a classic bug: example.com.attacker.test ends with the wrong text but starts with the right one, and a loose check will pass it.

The mistake to avoid

The shortcut is to copy whatever Origin arrives into the header and add credentials. That tells every website on the internet it may read authenticated responses from your API in your users' browsers. It looks like a wildcard fix and behaves like a security hole.

If an endpoint is truly public and uses no cookies, * is fine, and simpler. Keep that endpoint separate from anything that reads a session.

Check your setup

  • List every endpoint that sets or reads a cookie.
  • For each, confirm the origin allowlist is explicit and short.
  • Confirm responses carry Vary: Origin.
  • Test with a deliberately wrong origin and expect no CORS headers back.

Also keep SameSite in mind. A cookie set with SameSite=Lax will not travel on most cross-site fetches at all, so a credentialed CORS flow often needs SameSite=None; Secure, or a design that keeps the API on the same site.

Quick check

Can I send several origins in one header value?

No. The header takes one origin. Check the request Origin against an allowlist and return the matching one.

Is the wildcard ever fine?

Yes, for public resources fetched without credentials.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.