Your fetch call fails with a wall of red text about origins and policies. The server is fine, the URL is right, and nothing you change in your code helps. The browser is doing this deliberately, and once you know why, the errors stop being mysterious.
What Is CORS, in One Paragraph
CORS is how a server tells the browser which other sites may read its responses.
Cross-Origin Resource Sharing is a set of HTTP headers. The server sends them; the browser enforces them.
By default a page cannot read data from a different origin. CORS is the mechanism for granting exceptions, one origin at a time.
Nothing about it happens on your server beyond sending a header. All the enforcement is in the browser.
To see which headers a given endpoint returns, run it through our CORS tester.
The Same-Origin Policy Underneath It All
Two URLs share an origin only when scheme, host and port all match.
| URL | Same origin as https://example.com/app? |
|---|---|
| https://example.com/other | Yes — only the path differs |
| http://example.com/app | No — different scheme |
| https://api.example.com/app | No — different host |
| https://example.com:8443/app | No — different port |
That table explains most local development pain. Your app on port 3000 and your API on port 8080 are different origins, so CORS applies on your own machine.
The Same-Origin Policy is the default; CORS is the opt-out. The policy came first by about fifteen years.
Why Does CORS Exist? A Concrete Example
Without it, any site you visit could read your logged-in data from any other site.
Picture your bank at bank.com, where you are signed in with a session cookie. You then open a link to a site you do not trust.
That site runs a fetch to bank.com/api/accounts. Your browser attaches your cookie automatically, because that is what cookies do.
The Same-Origin Policy is what stops the attacker reading the reply. Without it, one careless click would expose every authenticated account you hold.
Seen that way, CORS is not an obstacle. It is the mechanism that lets the browser relax a rule you very much want by default.
What Actually Happens on a Blocked Request
The response is discarded after it arrives, not before it is sent.
For a simple request the sequence is:
- Your code calls fetch.
- The browser sends the request, including an Origin header.
- The server replies.
- The browser checks for Access-Control-Allow-Origin.
- No match, so it throws the response away and reports an error.
Step three is the part people find surprising, and it has a real consequence. If that request deleted a record, the record is gone — you just cannot read the confirmation.
This is exactly why CSRF protection is a separate concern. CORS governs reading, not doing.
The Meaning of a Preflight Request
For anything beyond a plain request, the browser asks permission before sending.
It fires an OPTIONS request first, describing what it intends to do. Only a satisfactory answer lets the real request proceed.
Three things trigger it: a method other than GET, HEAD or POST; any non-safelisted header such as Authorization; or a JSON content type.
That third condition means nearly every modern API call is preflighted. We cover the mechanics in how preflight requests work.
What CORS Does Not Do
It is not a firewall, and treating it as one is a genuine risk.
- It does not stop non-browser clients. curl, Postman and any server-side script ignore it completely.
- It does not prevent the request. Side effects still happen.
- It does not authenticate anyone. You still need real auth.
- It does not stop CSRF. Different problem, different defence.
We say this because "our API is protected by CORS" comes up in review more often than it should. A restrictive CORS policy on an unauthenticated endpoint protects nothing.
Once you are ready to send real requests with auth, our API tester handles the full round trip.
Wrapping Up
CORS is the browser asking a server whether one site may read another site's response. The server answers with headers, and the browser obeys.
Remember the two things it is not: not a server-side protection, and not a defence against actions. It governs reading, in browsers, only.
Hitting a specific error? Start with the missing origin header, or look up any header in our complete CORS headers reference.
