By default a page may send requests to any site but may only
read replies from its own origin (scheme + host + port).
CORS (Cross-Origin Resource Sharing) is how a server tells the browser
"pages from other origins may read this". It's all done with
access-control-* headers, and the browser, not the server,
enforces it. Notes.
New to requests, methods and headers? Start with HTTP
and fetch.
This page's origin:
Each button runs the fetch shown, from this page, against
Rooms (a val.town server
that sends the three headers below) or example.com (which sends none).
Open DevTools' Network tab to watch the preflight
OPTIONS requests, and the Console for the browser's
explanation of each failure, which fetch itself never gets.
access-control-allow-origin: * access-control-allow-methods: GET, POST, OPTIONS access-control-allow-headers: content-type
A model of the browser's checks. Describe a request and a server's headers, and it shows what the browser sends, what it checks, and whether the page gets to read the reply. Start from a preset, then change one thing at a time.
The browser adds the request headers itself; page code can't set or fake them. The server sends the response headers.
| Header | Common values | What it does |
|---|---|---|
| Sent by the browser | ||
origin | https://backspaces.github.io, null |
Who is asking: the page's origin. null for pages opened from a file, sandboxed frames and some redirects. |
access-control-request-method | PUT |
Preflight only: the method the real request will use. |
access-control-request-headers | content-type,x-api-key |
Preflight only: the non-simple headers the real request will carry, lowercase and sorted. |
| Sent by the server | ||
access-control-allow-origin | *, or one exact origin |
Which origin may read the reply. Exactly one value: * or a single origin. To allow a list, the server checks
origin against its list and echoes back the matching one. Required on every reply, preflight or not. |
access-control-allow-methods | GET, POST, PUT, DELETE, * |
Preflight only. Methods other than GET, HEAD and POST that are allowed. Those three always pass. |
access-control-allow-headers | content-type, authorization, * |
Preflight only. Non-simple request headers that are allowed. * doesn't cover authorization, which must be named. |
access-control-allow-credentials | true (or absent) |
Allows a page that sent cookies (credentials: "include") to read the reply. Then every * stops working as a wildcard. |
access-control-expose-headers | x-request-id, etag, * |
Response headers the page may read beyond the basic ones (content-type, content-length, cache-control, content-language, expires, last-modified, pragma). |
access-control-max-age | 600, 7200, 86400 |
Preflight only. Seconds the browser may reuse this preflight answer. Absent means 5 s. Chrome caps it at 2 hours, Firefox at 24. |
vary | origin |
Not a CORS header, but needed when allow-origin echoes the caller. It tells caches the reply differs per origin. |