Start with one question: did the browser send the actual request? A failed CORS preflight can prevent it. In other cases the server receives the request, but browser JavaScript cannot read the response. Repeating an operation before distinguishing those cases can repeat its side effects.

curl success does not establish browser permission to read a response. WHATWG CORS protocol

This guide is for developers diagnosing an API they own or are authorized to integrate. It reviews documentation checked November 1, 2026; the Fetch Living Standard displayed an October 19, 2026 update. MDN and OWASP are living references, not pinned browser releases. The trace below is fictional, not a captured request or reproduced experiment.

Which request failed: OPTIONS or the actual operation?

Open the browser’s Network panel and console. Record the page origin, target URL, request method, and the reported error. JavaScript generally receives limited CORS failure details; the console is the better starting point. MDN CORS errors

Diagnostic sequence: inspect OPTIONS if present, then the actual request, then JavaScript response access. Absence of OPTIONS alone proves neither success nor failure.

Original diagnostic map. Arrows are inspection order, not a captured network sequence.

  • No matching request: nothing yet establishes a server CORS issue. Check the console, URL, application execution, and browser/network blocking before changing server headers.
  • OPTIONS fails; no actual request: the operation may be stopped at preflight. Inspect the requested method/header names and the preflight response.
  • Actual response present, but fetch rejects: a server response alone does not establish JavaScript access. Inspect the actual response’s CORS headers and console details, including error responses.
  • Body readable, one header unavailable: check Access-Control-Expose-Headers for non-forbidden headers; Set-Cookie cannot be exposed. This is narrower than an unreadable response.

Not every cross-origin request needs preflight, and a valid preflight can be cached. No OPTIONS row is not a diagnosis. Conversely, a successful OPTIONS response does not certify the subsequent response. The two responses must satisfy their respective protocol checks. MDN CORS guide

For your incident note, keep separate fields for “actual request observed,” “server outcome confirmed,” and “response readable.” Treat an unknown server outcome as unknown rather than automatically retrying a write. Our webhook crash-window guide covers the adjacent problem of acknowledging work without duplicating it.

What should a preflight contain—and what should it not contain?

Fictional trace: https://app.example reads a sandbox status endpoint using an explicit bearer header and credentials mode omit (no browser-managed cookies). The browser asks about the method and header name:

OPTIONS /status HTTP/1.1
Origin: https://app.example
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization

Illustrative headers, not a runnable command. No bearer token goes in OPTIONS. A possible affirmative response:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: Authorization
Vary: Origin

Authorize the actual GET separately and include its appropriate origin-sharing header. Preflights exclude credentials; do not require its bearer token on OPTIONS. Enterprise TLS-client-certificate exceptions are outside this example. Fetch rules

Do not copy a production POST into a terminal just to “test CORS.” Use an authorized, genuinely read-only endpoint or an isolated fixture. Even a GET endpoint should be checked for side effects before replay. Do not paste cookies or tokens into a ticket or public console transcript. For evidence sharing, use the minimization checklist in our HAR-file guide.

Why can login requests fail even when the origin header exists?

Credentials mode include requires an allowed explicit origin and Access-Control-Allow-Credentials: true, not wildcard origin—even if no cookie was observed. Fetch credentials table

Do not fix this by reflecting every incoming Origin. Maintain the server’s intended allowlist, and distinguish the application origin from the API URL. Nor should you remove credentials simply to silence the error if authentication depends on them. Cookie delivery is a separate question: browser third-party-cookie policy can still restrict it despite correct CORS headers. MDN credentialed requests

A useful support report therefore records credentials mode and whether a cookie was sent as separate facts, without recording the cookie value. If a 401 or 500 response lacks the expected CORS headers, investigate both the underlying failure and the error-response path; permitting every origin is not an authentication repair.

Can no-cors, a proxy, or caching make the problem disappear?

mode: 'no-cors' is not a switch that lets application JavaScript read arbitrary cross-origin JSON. Its opaque response does not expose the response body or headers to that script. MDN Request.mode

A same-origin backend can be an intentional architecture, but our recommendation is to treat it as a separate authenticated service boundary—not an emergency open relay. Decide which upstreams it may contact, whose authorization it uses, and what information it returns before building it.

When a server selects response headers according to Origin, Vary: Origin tells caches that this request header participates in choosing the response variant. It does not by itself establish private caching, authorization, or safe storage of user data. Review those controls separately. MDN Vary

Does a CORS error mean the operation was safe?

No. Some cross-origin requests can reach a server without preflight, and blocking script access to a response does not undo server work. OWASP discusses CSRF protections for such requests and a conditional custom-header approach for APIs; strict allowed origins and server-side checking are important conditions. Neither “CORS prevents all CSRF” nor “CORS is irrelevant to every CSRF defense” is a useful universal rule. OWASP CSRF guidance

Our stopping rule: do not loosen production policy until you can name the failing stage, intended caller, and minimum required access. Keep application authentication and authorization intact. A diagnosis is complete when you have evidence for both the operation’s outcome and the browser’s permitted response access—not merely a green curl command.

Sources & scope

Primary-documentation review with an original decision diagram and fictional HTTP trace. No live API probes, browser reproduction, or security experiments were performed.

  1. WHATWG Fetch — CORS protocol
  2. MDN — CORS
  3. MDN — CORS errors
  4. MDN — Request.mode
  5. MDN — Vary
  6. OWASP — Cross-Site Request Forgery Prevention Cheat Sheet
Publishing details & corrections

Prepared with AI assistance. Sources, any tests performed, and their limitations are described in the article.

No material corrections recorded for this version.

Send a correction

Back to writingEditorial standards