Questions › Networking/Security
What problem does CORS solve, and how does the preflight request work? Which headers matter?
Understanding CORS as a server-controlled relaxation of SOP, and the mechanics of preflight most developers hit.
CORS (Cross-Origin Resource Sharing) is the HTTP-header mechanism by which a SERVER opts in to letting specific other origins READ its responses — a controlled relaxation of the same-origin policy. Without CORS, a browser will SEND a cross-origin request (say, app.com's JS fetching api.other.com) but will BLOCK the JavaScript from reading the response unless the server says it's allowed. The server signals permission with response headers, chiefly Access-Control-Allow-Origin: either the specific requesting origin (echoed back) or '*' (any origin). Crucial subtlety: for requests that send CREDENTIALS (cookies, Authorization), the server must send Access-Control-Allow-Credentials: true AND cannot use '*' for the origin — it must name the exact origin. For 'non-simple' requests — anything using methods beyond GET/POST/HEAD, or custom headers, or a Content-Type like application/json — the browser first sends a PREFLIGHT: an automatic OPTIONS request asking 'may I make this actual request?', carrying Access-Control-Request-Method and Access-Control-Request-Headers. The server responds (with no body) listing what it permits via Access-Control-Allow-Methods, Access-Control-Allow-Headers, and optionally Access-Control-Max-Age (how long the browser may cache this preflight so it doesn't re-ask every time). Only if the preflight approves does the browser send the real request. 'Simple' requests (GET/POST with standard headers and form/text content types) skip preflight. The mental model that clears up most confusion: CORS is enforced BY THE BROWSER to protect the USER, and it's granted BY THE SERVER; it is NOT a server-side access control (a non-browser client like curl ignores CORS entirely). So CORS errors are the browser refusing to hand YOUR script a cross-origin response the server didn't authorize — the fix is on the SERVER (send the right Allow headers), never 'disable CORS in the browser'.
Debugging a CORS error; designing an API consumed cross-origin; deciding when preflight fires.
Non-simple request (e.g. JSON PUT) triggers a PREFLIGHT:
Browser --OPTIONS--> Server
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: content-type
Server --200------> Browser (no body)
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, PUT
Access-Control-Allow-Headers: content-type
Access-Control-Max-Age: 600 (cache the preflight)
...then the REAL PUT is sent.
Credentialed requests: Allow-Credentials: true AND a specific origin (never '*').
CORS is enforced by the BROWSER, granted by the SERVER. curl ignores it.
Fix CORS errors on the SERVER, not the browser.