Fix Credentialed CORS: Check Origins, Preflight and Cookies

Trace a credentialed cross-origin request through OPTIONS and the actual response, then test allowed origins, rejected origins and cookie behavior.

When a browser request uses credentials mode include, Access-Control-Allow-Origin must identify the allowed origin rather than *. The response also needs Access-Control-Allow-Credentials: true. Authorization-header handling and cookie acceptance have additional rules.

Author: Evan•Published: March 18, 2026•Updated: October 9, 2026•2 min read

Tools in this guide

Symptoms

  • Browser console reports CORS failure despite server returning 200.
  • A browser fetch with credentials: include fails while a cURL request is readable.
  • Preflight passes inconsistently across origins.

Root Cause

  • The request uses credentials mode include while the response allows wildcard origin.
  • Origin reflection is configured but cache layer misses Vary: Origin.
  • Allow-Headers / Allow-Methods do not match real frontend request.

Fix Steps

  1. Set explicit allowed origin (or controlled origin reflection) instead of wildcard.
  2. Generate headers for one static allowed origin. The generator does not implement a dynamic allowlist; if your server selects an origin per request, implement that check there and set Vary: Origin.
  3. Recheck request/response header blocks and validate cookie attributes if session auth is used.

Response headers for a specific allowed origin

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Vary: Origin

FAQ

Can I keep wildcard with credentials?

No. Browsers block this combination by design.

Do I always need Vary: Origin?

You need it whenever origin is dynamic to avoid cache confusion.

1. Separate the preflight from the actual response

Consider a page at https://app.example.com sending a POST with Content-Type: application/json, Authorization and credentials: include to an API on another origin. The browser normally sends OPTIONS first, advertising POST and the requested non-safelisted headers. Inspect that request and the subsequent POST separately.

For this example, a successful preflight allows the exact origin, POST and the explicit header names Content-Type and Authorization, together with Access-Control-Allow-Credentials: true. The actual response also needs the origin and credentials headers. A 204 OPTIONS response alone does not make the POST readable.

2. Test an allowed origin and a rejected origin

With a disposable test endpoint, send one browser request from the allowlisted origin and one from an origin that is not allowed. The server should not echo arbitrary Origin values. Expect the allowed response to be readable and the disallowed response to lack a grant that lets browser JavaScript read it.

A CORS failure does not prove the server never received or processed a request. Some requests do not need preflight, and application authentication and CSRF controls remain separate. Do not use a real payment or other consequential endpoint for this test.

3. Check the cached response and cookie decision

If the server chooses an origin per request, return Vary: Origin and ensure any CDN cache policy respects it. Inspect the final headers after gateways; duplicate or conflicting Access-Control-Allow-Origin values can still fail.

For cookie sessions, inspect the browser’s actual cookie exclusion reason, scope and credentials mode. SameSite=None; Secure can be necessary for cross-site use but does not override third-party-cookie blocking. cURL can reveal response headers but does not enforce browser CORS or cookie policy.