Watch at EaseStories. Answers. Perspective.

Technology · PRACTICAL GUIDE

Browser CORS failure? Prove preflight, the actual response, and the origin contract

A browser request can fail before the API call, after a successful OPTIONS exchange, or only when credentials are involved. This evidence-first workflow shows how to identify the exact failing hop without guessing at headers.

Original diagram showing OPTIONS, Origin, Allow and Actual stages in a browser CORS request chain.
Original illustration: prove the preflight and the actual response as separate exchanges. Watch at Ease

When curl or Postman works but browser JavaScript reports a CORS error, treat the browser exchange as a chain, not a single request. Capture the Network panel and prove three separate facts: whether a preflight was sent, whether the actual response carried a readable CORS contract, and whether credentials require an exact origin. Each branch has a different fix.

Start with the browser request chain

Open the browser's Network panel, preserve the log, reproduce the failure once, and filter by the API host or path. Do not begin by changing server headers. First decide whether the browser sent an OPTIONS preflight, sent only the actual request, or stopped before either request reached the server. A successful curl or Postman call proves that an HTTP endpoint can answer that client; it does not prove that the browser will expose a cross-origin response to script.

Audit the OPTIONS preflight as its own exchange

If an OPTIONS row exists, inspect its request and response separately. The browser normally sends Origin plus Access-Control-Request-Method and, when needed, Access-Control-Request-Headers. The response must allow the requesting origin, method and requested headers. A 200 or 204 response can be valid, but a redirect, authentication challenge, network failure or missing allow header means the browser will not send the actual request. Compare the exact requested header names instead of adding a broad list by guesswork.

Then audit the actual response separately

A passing preflight only authorizes the browser to send the real request. The actual response still needs a matching Access-Control-Allow-Origin value before JavaScript may read it, including on error responses. Therefore, inspect both network rows: an API may return 401, 404 or 500 and still need the CORS response header. If the response body is readable but a custom response header is missing from JavaScript, check Access-Control-Expose-Headers rather than changing preflight rules.

Prove the credentials and origin contract

For fetch requests that include cookies or HTTP authentication, the server cannot answer with Access-Control-Allow-Origin: *. It must return the exact requesting origin and Access-Control-Allow-Credentials: true. The preflight itself is sent without credentials, so middleware that demands an authenticated OPTIONS request can block the chain before the credentialed request begins. Cookies also remain subject to SameSite and third-party-cookie policy; correct CORS headers do not override those browser rules.

Check dynamic origins, caches and middleware order

When a server chooses an allowed origin dynamically, return Vary: Origin so a shared cache does not reuse one origin's response for another. Confirm that CORS middleware runs for OPTIONS and for error paths, not only after authentication or a successful route handler. In Express, application-level cors middleware handles preflight for all routes; route-level configurations may need an explicit OPTIONS handler. Verify deployed behavior in the Network panel instead of assuming the local configuration reached the running service.

Use a stop-branch decision table

No OPTIONS and no actual request points to a URL, mixed-content, extension, DNS or other network-layer problem rather than a CORS response. OPTIONS present but no actual request means fix the preflight status or allow-method/header/origin contract. Both requests present but JavaScript cannot read the result means inspect the actual response's allow-origin and credential headers. A readable body with hidden custom headers means expose only the response headers the application needs. This branch order prevents a working preflight from hiding a broken actual response—and prevents a server fix from being attempted for a request the browser never sent.

Original diagram contrasting credentials, wildcard origin, exact origin and Vary Origin rules.
Original illustration: credentialed requests require an exact allowed origin, and dynamic origin responses should vary by Origin. Watch at Ease