Why Access-Control-Allow-Origin: * Fails with Credentials
Reviewed September 3, 2026 · Maintained by William
The combination Access-Control-Allow-Origin: * and credentialed browser requests is intentionally restricted. A wildcard says “any origin,” while credentials can include cookies, client certificates or HTTP authentication tied to a user. Browsers require a more specific trust decision.
The failing pattern
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
If client JavaScript sends a credentialed request, the browser will not expose that response to the page under the wildcard policy.
The safer pattern
Maintain an allowlist and return the exact allowed origin:
Origin: https://app.example.com
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Vary: Origin
If the request comes from an untrusted origin, do not emit a permissive allow-origin response.
Why blindly reflecting Origin is also dangerous
A common shortcut is to read the incoming Origin header and echo it back. Without validation, that turns any requesting website into an allowed origin and defeats the purpose of the restriction. Reflection is only safe when it follows a real allowlist check.
Credentials do not mean “Authorization header only”
Fetch credentials mode affects browser-managed credentials such as cookies. Custom Authorization headers have their own preflight implications. Treat both authentication and CORS as separate controls: first decide which origin may call the API from a browser, then decide which user/service is authorized.
Practical server rule
- Keep a normalized set of approved origins.
- Compare the request Origin against that set.
- Return the exact matched origin and
Vary: Origin. - Return
Access-Control-Allow-Credentials: trueonly when the application actually needs credentialed cross-origin requests. - Reject or omit CORS approval for every other origin.
The CORS Header Generator deliberately warns about wildcard + credentials because a generator should not encourage a combination browsers reject.
References
The credentialed-request rule in practical terms
When browser JavaScript sends credentials—such as cookies, HTTP authentication or a credentialed fetch mode—the server cannot combine that behavior with a wildcard Access-Control-Allow-Origin: * response and expect the browser to expose the result. The server needs to return a specific allowed origin and, when appropriate, Access-Control-Allow-Credentials: true.
fetch('https://api.example/profile', {
credentials: 'include'
});
For a request originating at https://app.example, an allow-listed response can look like:
Access-Control-Allow-Origin: https://app.example
Access-Control-Allow-Credentials: true
Vary: Origin
Why blind origin reflection is not an allow-list
A dangerous pattern reads the request Origin header and writes it straight back to Access-Control-Allow-Origin. That produces a specific-looking value, but it effectively approves every requesting website. Instead, normalize and compare the incoming origin against a configured set of exact origins before returning it.
allowed = {
"https://app.example",
"https://admin.example"
}
if request.origin in allowed:
response.allow_origin = request.origin
The actual implementation depends on your framework, but the policy should be explicit. Be cautious with substring checks such as “ends with example.com”; hostile names like example.com.attacker.test can defeat sloppy matching.
Cookies add another policy layer
Even correct CORS headers do not override cookie rules. Cross-site cookies can be affected by SameSite, Secure, domain/path attributes and browser privacy behavior. If a credentialed request reaches the API without the expected cookie, inspect cookie storage and request headers before changing CORS.
Debugging sequence
- Confirm the exact origin shown by the browser, including scheme and port.
- Check whether the request is credentialed.
- Inspect the preflight response if one exists.
- Inspect the final response, not just OPTIONS.
- Verify that the returned allow-origin value is one explicitly trusted by the API.
- For dynamic origins, verify
Vary: Originso shared caches do not serve one origin’s CORS response to another. - Only then investigate cookie
SameSiteor authentication/session behavior.
When a wildcard is appropriate
A wildcard can be appropriate for genuinely public, non-credentialed resources where any website may read the response. That is a product/security choice, not a shortcut to make errors disappear. If a public endpoint later starts returning user-specific or session-bound data, revisit the policy rather than leaving the old wildcard in place.