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-OriginAccess-Control-Allow-HeadersAccess-Control-Allow-MethodsAccess-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.