Concept · The browser
Same-origin policy and CORS
The browser keeps pages from reading other origins’ responses; CORS headers are how a server opts in.
An origin is scheme + host + port: https://app.example.com:443. Change any of the three and it's a different origin. Note that https://example.com and https://api.example.com are different origins.
The same-origin policy is the browser's core security rule: a page can send requests almost anywhere, but script can't read the response from another origin unless that origin allows it. Without the rule, any site you visit could fetch('https://yourbank.com/account') with your cookies attached and read your balance.
CORS: the server opts in
Cross-Origin Resource Sharing is a set of response headers through which a server tells the browser which other origins may read its responses:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Vary: OriginFor anything beyond a "simple" request, such as PUT/DELETE, a JSON Content-Type, or custom headers like Authorization, the browser first sends a preflight:
OPTIONS /orders HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: content-type, authorizationThe server must answer with matching Access-Control-Allow-Methods and -Headers. Access-Control-Max-Age lets the browser cache the answer so it doesn't send a preflight every time.
Misconceptions
- CORS doesn't protect your server. It protects users' browsers from malicious pages.
curl, scripts and other servers ignore it completely. You still need authentication and authorization. - A CORS error doesn't mean the request didn't happen. A simple
POSTis sent and processed; the browser just hides the response. Protect state-changing endpoints against CSRF withSameSitecookies or tokens. See sessions vs tokens. - Don't reflect any
Originwith credentials allowed. That turns the policy off for every site. Keep an allowlist of origins. *can't be combined with credentials. The browser rejects that combination.
Often you don't need it
If the frontend and API share an origin, for example with the API under /api behind the same host or a dev-server proxy, there's no cross-origin request and nothing to configure. When you do need CORS, set it in one place: middleware or your API gateway.
The same-origin policy limits what other sites can read. XSS is the opposite problem: an attacker's script running inside your origin, where the policy doesn't help.