CORS explained
Why the browser blocks cross-origin reads, how preflight works, and the headers that fix it properly.
The problem CORS solves
Your browser attaches cookies and tokens to requests automatically. Without protection, any site you visited could read data from another service you are logged into β your bank, your mail. The same-origin policy blocks reading cross-origin responses; CORS is how a server explicitly relaxes that.
Simple requests vs preflight
A request using only GET/HEAD/POST with simple headers is sent directly. Anything else β custom headers, PUT/PATCH/DELETE, JSON bodies β triggers a preflight OPTIONS request asking permission first.
OPTIONS /api/item HTTP/1.1
Origin: https://app.example
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: authorization
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Headers: authorization
Access-Control-Max-Age: 600The response headers
| Header | Purpose |
|---|---|
Access-Control-Allow-Origin | Permitted origin, or * (never with credentials) |
Access-Control-Allow-Credentials | Allow cookies β requires an explicit origin |
Access-Control-Allow-Methods | Allowed methods for preflight |
Access-Control-Allow-Headers | Allowed request headers |
Access-Control-Max-Age | Cache preflight result (seconds) |
Vary: Origin | Essential when the origin varies per caller |
Access-Control-Allow-Origin: * combined with Allow-Credentials: true is rejected by browsers β a wildcard cannot authorise credentials. Echo the specific origin instead, and always send Vary: Origin.Fixing it in practice
A CORS error is always fixed on the server that owns the resource. No amount of fetch options or browser flags will bypass it.
// Express: allow one trusted app
app.use(cors({
origin: 'https://app.example',
credentials: true
}));
// fetch side: include cookies when needed
fetch(url, { credentials: 'include' });mode: 'no-cors'does not fix anything β it gives you an opaque response you cannot read.- During development, proxy through your dev server instead of opening your API to every origin.
- A reverse proxy serving API and app on one origin removes CORS questions altogether.
FAQ
Why does it work in curl but fail in the browser?
Can I just use a browser extension?
Related
HTTP headers Async JavaScript and fetch
Last refreshed 2026-09-17.