CORS: Origins, Headers & Preflight Requests
CORS is the browser-enforced relaxation of the Same-Origin Policy -- by default a page's JavaScript can't read a response from a different origin, protecting users from a malicious site silently reading data from another site using the browser's existing cookies. CORS lets a server explicitly opt certain origins in.
What Counts as an Origin
Origin = scheme + host + port, all three must match exactly.
http://example.com and https://example.com -- different origins (protocol differs).
http://localhost:3000 and http://localhost:8080 -- different origins (port differs), a very common local-dev gotcha.
The Core Response Header
# Server response -- explicitly allows this origin to read the response
Access-Control-Allow-Origin: https://app.example.com
# Without this header (or a mismatch), the browser BLOCKS the
# requesting page's JavaScript from accessing the response -- but
# for a simple request, the server may have already fully processed
# it (e.g. a POST could still write to the database). CORS blocks
# response VISIBILITY to JS, not necessarily server-side execution.
# Wildcard -- fine for genuinely public, non-sensitive data
Access-Control-Allow-Origin: *
# For sensitive/user-specific data, reflect back a checked, allowed
# origin instead of a blanket wildcard
Vary: Origin
Access-Control-Allow-Origin: https://app.example.com # only if it matched an allowlistPreflight: The OPTIONS Round-Trip
# "Simple" requests skip preflight: GET/HEAD/POST, only safelisted
# headers, and Content-Type limited to form-encoded/multipart/plain-text.
# A JSON POST (Content-Type: application/json) does NOT qualify --
# it triggers a preflight, a common surprise.
# Browser sends automatically, BEFORE the actual request:
OPTIONS /api/users HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Authorization, Content-Type
# Server must respond declaring what it permits:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400 # cache preflight result, skip repeat OPTIONS callsKeep your own version of these notes — editable, searchable, and organised by your stack.
Start free