CORS
01 / 02

Origins, Headers & Preflight Requests

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 allowlist

Preflight: 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 calls

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free