CORS

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:

1. Real requests

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

2. What if…

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.

Presets:
The page's request
The server's CORS headers (blank = not sent)

3. The headers

The browser adds the request headers itself; page code can't set or fake them. The server sends the response headers.

HeaderCommon valuesWhat it does
Sent by the browser
originhttps://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-methodPUT Preflight only: the method the real request will use.
access-control-request-headerscontent-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-methodsGET, POST, PUT, DELETE, * Preflight only. Methods other than GET, HEAD and POST that are allowed. Those three always pass.
access-control-allow-headerscontent-type, authorization, * Preflight only. Non-simple request headers that are allowed. * doesn't cover authorization, which must be named.
access-control-allow-credentialstrue (or absent) Allows a page that sent cookies (credentials: "include") to read the reply. Then every * stops working as a wildcard.
access-control-expose-headersx-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-age600, 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.
varyorigin Not a CORS header, but needed when allow-origin echoes the caller. It tells caches the reply differs per origin.