Developers configuring CORS often set Access-Control-Allow-Origin: * assuming it will grant access to all clients. However, as soon as authentication cookies or credentials are attached, the browser reports a failure: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.

What browser credentials mean in CORS

In the context of the Fetch API and XMLHttpRequest, “credentials” refers to ambient or explicit user authentication tokens automatically managed by the browser:

  • HTTP cookies (session cookies, tracking cookies)
  • HTTP authentication headers (such as Authorization: Basic ...)
  • TLS client certificates

When client JavaScript executes fetch('https://api.example.test/user', { credentials: 'include' }), it instructs the browser to attach stored cookies for api.example.test to the cross-origin request.

Why wildcard origin and credentials conflict

Allowing a wildcard origin * with credentials would create a catastrophic security vulnerability:

If any website on the internet could make credentialed requests to api.example.test and read the responses via Access-Control-Allow-Origin: *, an attacker’s website could read the private personal data of any logged-in user who visits their site.

To protect user privacy, the CORS specification requires that any response exposing credentialed data must explicitly name the authorized origin.

Access-Control-Allow-Credentials

The Access-Control-Allow-Credentials header informs the browser whether the server permits sharing the response when credentials are sent.

Its only valid value is true (case-sensitive). If omitted, or set to false, the browser will not expose the response to frontend JavaScript, even if the origin matches.

Specific allowed origins and server ownership

To support credentialed cross-origin requests securely:

  1. The server reads the incoming Origin request header (e.g., Origin: https://app.example.test).
  2. The server verifies whether https://app.example.test exists in an internal allowlist of trusted origins.
  3. If valid, the server echoes that specific origin back in the Access-Control-Allow-Origin header:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.example.test
Access-Control-Allow-Credentials: true

Cookies, Authorization, and careful distinctions

Note the following distinctions:

  • Session cookies: Always require credentials: 'include' and explicit origins.
  • Bearer tokens: When tokens are stored in JavaScript memory and passed via Authorization: Bearer <token>, they do not technically require credentials: 'include', but they do require Access-Control-Allow-Headers: Authorization and preflight approval.

What to inspect in DevTools

  1. Open DevTools > Network tab.
  2. Select the failed request.
  3. Look at Response Headers:
    • Check if Access-Control-Allow-Origin is set to *.
    • Check if Access-Control-Allow-Credentials is missing or not set to true.
  4. Check Request Headers for Cookie or Authorization.

What not to do

  • Do NOT blindly reflect any Origin header: Echoing back arbitrary origins without validation effectively recreates the insecure wildcard behavior.
  • Do NOT use public CORS proxies: Passing credentials through third-party proxies exposes sensitive cookies and tokens.
  • Do NOT disable browser security: Bypassing security checks in local development masks production configuration failures.

Common causes

Server Uses Wildcard Origin on Credentialed Request

The backend server responds with Access-Control-Allow-Origin: * to a request sent with credentials: 'include', which the browser security model rejects.

Missing Access-Control-Allow-Credentials Header

The server returns an explicit origin in Access-Control-Allow-Origin but omits Access-Control-Allow-Credentials: true.

Frontend Credentials Flag Set Unintentionally

The client application sets credentials: 'include' on public, unauthenticated endpoints that were intended for generic wildcard access.

Reverse Proxy Replaces Explicit Origin with Wildcard

An intermediate edge proxy or CDN overrides backend CORS response headers with a static wildcard origin.

What to check before changing code

  1. Check request credentials mode in frontend code: Verify whether the fetch or XMLHttpRequest call explicitly includes credentials: 'include' or withCredentials = true.
  2. Inspect response headers: Confirm whether the backend returned Access-Control-Allow-Credentials: true and an exact origin match rather than *.
  3. Review origin allowlist logic: Ensure the backend validates the requesting origin against a trusted list before returning it.

How to verify the fix

Verify server responses using curl with a simulated origin:

curl -i -H "Origin: https://app.example.test" \
  https://api.example.test/v1/user

Confirm that the response contains Access-Control-Allow-Origin: https://app.example.test and Access-Control-Allow-Credentials: true.

Key takeaway

When cross-origin requests include credentials, the browser rejects Access-Control-Allow-Origin: *. The server must validate the requesting origin against an internal allowlist, return the explicit origin string in Access-Control-Allow-Origin, and set Access-Control-Allow-Credentials: true.