A CORS error means the browser would not expose a cross-origin response to the calling script under the applicable rules. It does not necessarily mean the request never reached the server. For some requests, the server may already have performed work before the browser blocks the response from JavaScript.
Debug the request in sequence: identify its origin, determine whether a preflight occurred, inspect the preflight response, then inspect the actual response and credential settings. This separates an OPTIONS routing failure from a missing header on an otherwise successful API response.
Write down both origins
An origin consists of scheme, hostname and effective port. https://app.example and https://api.example are different origins, even when the same team owns them. A development frontend on port 3000 is also different from an API on port 8000.
The CORS policy analyzer reviews pasted headers and identifies common contradictions. It does not send the browser request or know your server's allowlist. Use it alongside the actual Origin request header and network trace.
Keep CORS separate from DNS and TLS. The browser must first reach an acceptable network endpoint before it can evaluate response access. A failed certificate or unreachable server may surface near a CORS message, but adding Access-Control-Allow-Origin will not repair the underlying connection.
Recognise when OPTIONS is the first relevant request
Some cross-origin requests require a preflight because of their method, headers or content type. The browser sends OPTIONS with the requesting origin and information about the intended method and non-safelisted headers. The server must return a response that permits the intended request under CORS rules.
A JSON POST commonly triggers a preflight because application/json is not one of the safelisted content types for this purpose. An Authorization header also changes the preflight requirements. Compare the actual request configuration instead of assuming all POST requests behave alike.
Preflight requests are sent without ordinary request credentials under the CORS protocol, so middleware that demands an authenticated application session for OPTIONS can block the request before the actual operation is attempted. Handle preflight deliberately while retaining proper authentication on the real API operation.
Origin: https://app.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type, authorization
These are illustrative preflight request fields. The response must permit the relevant origin, method and headers. Returning a generic 200 page without the required CORS information does not authorise the subsequent request.
| Observation | Likely layer | Check |
|---|---|---|
| OPTIONS returns 401 | Preflight routing or authentication middleware | Handle preflight before ordinary session requirements |
| OPTIONS passes, POST is blocked | Actual response policy | Headers on success, error and redirect responses |
| Wildcard with credentials | Origin authorisation mismatch | Use the permitted explicit origin under credential rules |
| Response readable, custom header missing | Response-header exposure | Access-Control-Expose-Headers |
| Works for one frontend only | Origin allowlist or cache variation | Exact origin matching and Vary behaviour |
Make credential intent explicit
Browser fetch credentials settings affect whether cookies and other relevant credentials accompany a cross-origin request. The server's CORS response must also allow credentialed access where required. Client and server settings are complementary; changing only one side may leave the flow broken.
A wildcard Access-Control-Allow-Origin is not compatible with exposing a credentialed response in the usual credentialed CORS case. Return an explicitly allowed origin and the appropriate credentials header when your design permits it. Do not blindly reflect any Origin value supplied by the caller.
CORS permission does not override SameSite or third-party cookie restrictions. A response can allow credentials while the browser still withholds a cookie under separate cookie policy. Inspect the outgoing Cookie header and browser cookie explanations before changing CORS repeatedly.
Trace a JSON API call that fails after login
Suppose a frontend sends a JSON POST with credentials to an API. The preflight succeeds, but the actual request returns a 403 response without Access-Control-Allow-Origin because an error handler bypasses the normal header middleware. JavaScript sees a CORS failure instead of the application's explanatory body.
Inspect the actual status and response in the network panel. Apply the intended CORS handling consistently to relevant success and error responses, while keeping the 403 and its authorisation meaning intact. Making the response readable must not turn a denied operation into an allowed one.
Repeat with an expired session, invalid input and an internal error. These paths often travel through different middleware. A successful happy-path request does not establish that the API exposes useful, correctly scoped errors when something goes wrong.
If the request never proceeds beyond OPTIONS, repair that stage first. Do not spend time debugging the JSON body of a POST the browser did not send. The network sequence tells you which layer is ready for the next test.
Make caches vary with dynamic origin responses
If the response selects Access-Control-Allow-Origin based on an approved request origin, shared caches need the appropriate Vary: Origin behaviour. Otherwise a response prepared for one frontend can be reused for another with the wrong origin header.
Review the CDN and reverse proxy configuration as well as the application. A cache can ignore or replace headers, and an origin-only test may miss the public response. Keep cache keys and variation aligned with the actual authorisation logic.
Preflight caching is separate from ordinary response caching. Access-Control-Max-Age can affect reuse of preflight permission, subject to browser limits. When testing a policy change, account for cached preflight results and use a controlled fresh context where necessary.
Expose only the response fields scripts need
Even when a response body is readable, JavaScript cannot automatically read every response header. Access-Control-Expose-Headers identifies additional response fields the application may access under CORS rules. A download interface may need a specific filename header or request identifier.
Keep request-header permission and response-header exposure distinct. Access-Control-Allow-Headers concerns what the request may send. Access-Control-Expose-Headers concerns what the script may read from the response. Swapping them produces a policy that looks comprehensive but addresses the wrong direction.
Do not expose secrets merely to make debugging convenient. Review which fields the frontend actually needs, and keep sensitive server details out of public responses regardless of CORS. CORS is a browser access rule, not a server-side secrecy mechanism for non-browser clients.
Keep CORS separate from authentication and CSRF
A server must authenticate and authorise operations independently of whether a browser can read the response. Non-browser clients are not constrained by browser CORS enforcement. Some cross-site requests can also be sent without a preflight, so a blocked response does not guarantee no state change occurred.
Use CSRF protection appropriate to the application's credential model. Validate origins where that is part of the design, and reject unauthorised state changes server-side. An allowlist should contain exact trusted origins, with deliberate handling of unusual values such as null.
Avoid no-cors as a fix for an API response your script needs to read. It changes request constraints and produces an opaque response in the relevant cases. It does not grant access to a response that the server has not authorised through CORS.
Test an unapproved origin too
After the intended frontend succeeds, repeat the request from a controlled origin outside the allowlist. Confirm that the server does not reflect it as authorised and that state-changing operations still enforce their own authentication and CSRF rules. This negative case catches a common repair mistake: replacing a restrictive but broken policy with unconditional origin reflection. Keep the test origin synthetic and owned by the team so the check does not involve unrelated sites.
Frequently asked questions
Why does curl work while fetch fails?
A command-line client does not apply the browser's same-origin response-access rules in the same way. Its success proves the endpoint can respond to that client, not that the browser is authorised to expose the response to your script. Compare the browser's Origin and preflight sequence.
Can I set Access-Control-Allow-Origin in the request?
The relevant permission is supplied by the server's response. Adding that response-header name to the client request does not authorise access and may create another preflight requirement. Configure the server or the intended same-origin application architecture.
Does a CORS error mean the server did nothing?
No. The actual request may have reached the server and changed state before response access was blocked. Inspect the network sequence and server logs. This is why CORS cannot replace authentication, authorisation or CSRF controls.
Why does an Authorization header trigger OPTIONS?
It is not a CORS-safelisted request header, so the browser commonly needs preflight permission for it. Return the appropriate allowed headers for trusted origins, then validate the token on the actual request. Preflight approval is not token authentication.
Can I list several origins in one allow-origin value?
Do not use a comma-separated list as if it were a valid origin allowlist response. Evaluate the request origin against the server's configured list and return the appropriate single allowed value when authorised, with correct cache variation.
Why is a cookie still missing after CORS is fixed?
Inspect fetch credential mode, cookie scope, SameSite and browser third-party restrictions. CORS does not force the browser to attach a cookie that another rule withholds. Diagnose the cookie's acceptance and attachment separately from response readability.
What confirms the fix?
Verify the preflight if present, the authenticated actual request, readable intended response fields and correct denial for an unapproved origin. Include error paths and cache behaviour. A single successful OPTIONS response is only one stage of the workflow.