CORS
Why a browser blocks a webpage from freely talking to a different website's server, and how that server can explicitly allow it.
What is it?
By default, a browser is deliberately suspicious of a webpage that tries to fetch data from a different website than the one it was loaded from. If a page at shopping.example tries to make a request to bank.example, the browser will, by default, block the page from reading the response — even if the request itself technically went through — because there's no reason to assume bank.example wants some random other site reading its data on a visitor's behalf.
This default blocking is called the same-origin policy, and it's a core browser security protection. But plenty of legitimate use cases need exactly this kind of cross-site request — a public weather API that many different websites are meant to call, for example. So there needs to be a way for a server to say "actually, it's fine, this specific other site is allowed to read my responses."
That mechanism is called CORS — Cross-Origin Resource Sharing. A server that wants to allow requests from other origins includes specific response headers saying so, and the browser checks those headers before deciding whether to let the requesting page's JavaScript actually read the response. Without the right headers present, the browser blocks access to the response even though the server already sent it.
Explain like I'm 10
It's like a delivery being physically dropped off at your door, but you (the browser) refuse to let the person inside the house read what's in the package unless the sender included a note saying 'yes, this address is allowed to open it.'
Examples
A blocked cross-origin request
// Running on https://myapp.example
fetch("https://api.otherservice.example/data")
.then(res => res.json())
.catch(err => console.error(err));
// Console: blocked by CORS policy — no
// 'Access-Control-Allow-Origin' header presentThe request may well have reached the server and gotten a response, but the browser refuses to hand that response's data to this page's JavaScript because the server didn't explicitly allow this origin.
A server opting in
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://myapp.example
Content-Type: application/json
{"forecast": "sunny"}By including this header naming the exact origin allowed to read the response, the server tells the browser it's fine for https://myapp.example specifically to access this data. A value of * would allow any origin at all.
How it works
When JavaScript on one origin makes a request to a different origin, the browser attaches the requesting page's origin to the request and then inspects the response headers before deciding whether to expose the response body to that page's JavaScript. If the server's response includes an Access-Control-Allow-Origin header matching the requesting origin (or a wildcard allowing any origin), the browser lets the request through; otherwise, it blocks access to the response, even though the network request itself may have completed successfully. For certain kinds of requests, the browser first sends an automatic "preflight" check — a separate request asking the server what's allowed — before sending the real one.
Why does it exist?
CORS exists because the same-origin policy, while essential for security, was too restrictive for the many legitimate cases where sites genuinely need to share data across origins — public APIs, third-party widgets, services split across multiple domains. CORS gives servers an explicit, opt-in way to loosen that restriction only where they choose to, rather than the browser blocking everything cross-origin unconditionally or removing the protection altogether.
When to use it
You'll need to configure CORS anytime a browser-based frontend on one origin needs to call an API hosted on a different origin — a common setup, and something you'll set up on the server side, since it's the server's headers that grant permission.
When not to use it
CORS isn't relevant for requests made from a server to another server (no browser is involved, so there's no same-origin policy to enforce), and it isn't a substitute for actual authentication or authorization — allowing an origin via CORS only controls whether a browser can read the response; it says nothing about whether the request itself should be trusted or permitted for other reasons.
Common mistakes
Assuming a CORS error means the request never reached the server, when the server may have processed it fine — the browser is only blocking the response from being read.
Setting Access-Control-Allow-Origin to * on an API that also handles sensitive, authenticated requests, which can be an overly permissive default.
Confusing CORS with a security feature that protects the server — it's actually a browser-enforced protection for the calling page and its users.
Practice exercises
- Easy:
In your own words, explain why the same-origin policy blocks a page's JavaScript from reading a cross-origin response by default.
- Medium:
Given a fetch request that fails with a CORS error, list the response header that would need to be added on the server to fix it, naming the specific allowed origin.
- Hard:
Explain what a CORS preflight request is, why it's necessary for some requests but not others, and sketch what a server would need to respond with to satisfy it.
Interview questions
What problem does the same-origin policy solve?
It prevents a webpage's JavaScript from freely reading responses from a different origin's server by default, protecting users from malicious pages silently reading data from other sites they happen to be logged into.
What is CORS, and who configures it?
Cross-Origin Resource Sharing — a mechanism where a server includes response headers explicitly permitting specific other origins to read its responses. It's configured on the server, not the requesting page.
If a request fails due to CORS, did the server actually receive it?
Often yes — the request can reach the server and get processed normally; the browser is what blocks the calling page's JavaScript from reading the response, because the server didn't grant permission via the right header.