August 1, 2026
What Is CORS, and Why Do Your API Requests Fail?
CORS is a browser-enforced mechanism that uses a preflight OPTIONS request and a handful of Access-Control-Allow headers to decide whether a page may read a cross-origin response. It protects users rather than servers, and it does not apply to server-to-server calls at all — which clears up most of the confusion around it.

You write a fetch call from your front end to an API on a different domain, the request looks correct in every way, and the browser responds with a red error in the console mentioning CORS. The same API works fine from Postman or curl. This is the canonical CORS confusion, and it happens because the browser and a command-line tool apply different rules. CORS is a browser security feature, not a server-side one, and understanding what it actually does turns the error from a mystery into a one-line fix.
What CORS is actually protecting against
Browsers enforce the same-origin policy, which says that JavaScript running on one origin — the combination of scheme, host, and port — should not be able to read responses from a different origin without explicit permission. The rule exists because without it, any site you visit could make requests to your bank, your email, or your internal company tools using your logged-in cookies, and quietly read the responses back.
Cross-Origin Resource Sharing, or CORS, is the negotiated relaxation of that rule — a way for a server to declare "I am willing to be read by scripts from these other origins." Without that declaration, the browser blocks the response even though the request was sent and the response came back. This is the key point that catches people: the request usually reaches the server, and the server usually sends a response. The browser is the one refusing to hand the bytes to your JavaScript.
Preflights and the headers that decide everything
For "simple" requests — GET and POST with a small set of headers and a conventional content type — the browser sends the request directly and checks the response headers afterward. For anything more complex, it sends a preflight first. A preflight is an OPTIONS request that asks the server, in advance, whether the real request is allowed.
OPTIONS /api/users HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: authorization, content-type
The server responds with the corresponding Access-Control-Allow-* headers, and only if they match does the browser proceed to the actual request.
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Headers: authorization, content-type
Access-Control-Max-Age: 86400
A short list of headers covers almost every real CORS configuration. Access-Control-Allow-Origin names the origin allowed to read the response, or echoes the request origin back. Access-Control-Allow-Methods lists the HTTP verbs permitted. Access-Control-Allow-Headers permits specific request headers, which matters the moment you send an Authorization header or a JSON content type. Access-Control-Allow-Credentials controls whether cookies and HTTP auth travel with the request, and when set to true, the origin header cannot be a wildcard. Access-Control-Max-Age tells the browser how long to cache the preflight result, so it does not double every request with an OPTIONS call.
Try it: JSON Formatter
Format, validate, and minify JSON instantly in your browser — no upload, no signup.
The mistakes behind most CORS errors
Once the model is clear, the common failures fall into place. The first is sending a request with an Authorization header or a JSON content type without realizing that triggers a preflight — so you see an OPTIONS request in the network tab and assume something is wrong. It is not; it is the browser doing its job.
The second is configuring the server to allow the method and headers but forgetting Access-Control-Allow-Origin, or setting it to a wildcard while also sending credentials. The wildcard and credentials are mutually exclusive by design; if cookies must travel, you have to reflect the specific request origin.
The third is assuming the API must be broken because the same request works from curl. curl is not a browser; it does not enforce the same-origin policy and never sends a preflight. If a request works from curl and fails from a browser, the cause is almost always CORS, and the fix lives on the server, not in your front-end code.
The last is treating CORS as security for the server. It is not. CORS protects the user's browser from leaking cross-origin responses to scripts that should not see them; it does not authenticate the caller and it does not stop a non-browser client from hitting the endpoint directly. Real authorization still belongs in your API.
When CORS does not apply at all
Server-to-server calls do not have a CORS concept. A back-end service calling another API, a webhook receiver, a mobile app making requests — none of them enforce same-origin policy, because that policy is a browser behavior tied to a rendered page. If your only client is another server, CORS headers are unnecessary, and a missing Access-Control-Allow-Origin header is not a bug.
That said, if your API serves browsers at all, configure CORS deliberately rather than letting it default to deny. Send the precise headers you need, prefer a specific origin over the wildcard, and handle OPTIONS properly so preflights succeed. And when a request that should work still fails, decode any JWT you are sending with the JWT Decoder to confirm its contents, and pretty-print the response body with the JSON Formatter — half of supposed CORS errors are actually 400 responses the browser reported before you read the body.