The course
Reference
The "look it up fast" companion to the course. Three things live here: a glossary of the terms every page on this site uses, two reference tables of the CORS headers themselves, and a decision flowchart that walks "will this cross-origin request succeed?" end to end. Page 4 (The headers) teaches these slowly, in prose with a live playground; this page is the index you scan when you already know what you're looking for.
A note on which spec governs what, because the site is precise about this and the search results are not: CORS itself is a mechanism of the WHATWG Fetch Standard — there is no separate "CORS RFC". The notion of an origin (scheme + host + port) is defined by RFC 6454. The CORS-safelisted request/response headers are enumerated by the Fetch Standard too. The WebSocket handshake — relevant to page 10's demo — is not a Fetch mechanism: it is governed by the WebSocket section of the WHATWG HTML spec together with RFC 6455. Citing "the CORS RFC" is always wrong.
Glossary
These definitions match how the terms are used across Mental model, The headers, Credentials, and Opaque responses — not reinvented here.
- Origin
-
A scheme, a host, and a port together — never the path, query, or
fragment.
https://www.example.comandhttp://www.example.comare different origins (scheme); so arehttps://a.example.comandhttps://b.example.com(host); so are two services on different ports of one host. Defined by RFC 6454. - Same-origin
- Sharing all three of scheme, host, and port. The browser's default rule — the Same-Origin Policy — lets a page read any response from its own origin freely.
- Cross-origin
- Differing in scheme, host, or port. Reading a cross-origin response is what CORS gates; the request is still sent, and the server still processes it — only the page's JavaScript is withheld the result unless the server opted in.
- Same-site vs. cross-site
-
A coarser boundary than origin: two hosts are the same
site when they share a registrable domain.
a.example.comandb.example.comare different origins but the same site. Cookies are site-scoped (this is whatSameSitekeys on); CORS is origin-scoped. A request can be same-site yet still cross-origin — the split-domain case this whole site demos. - Registrable domain
-
The "site" a host reduces to: the public suffix plus one label (the
eTLD+1), computed against the
Public Suffix List. For
www.example.comit isexample.com; forapi.www.corslabs.comit iscorslabs.com. This is why every lab origin in this project is the same site when deployed. - Simple request
-
A request the browser sends without a preflight: a
CORS-safelisted method (
GET,HEAD, orPOST) carrying no headers outside the CORS-safelisted set and a safelistedContent-Type(application/x-www-form-urlencoded,multipart/form-data, ortext/plain). Only theOriginrequest header carries the CORS conversation for these. - Preflight / preflighted request
-
An
OPTIONSrequest the browser sends before the real request whenever the real request is not "simple". It carriesAccess-Control-Request-MethodandAccess-Control-Request-Headers; the server answers with its allowances, and only if they cover the real request does the browser send the real request at all. - CORS-safelisted request header
-
A request header the browser will send cross-origin without promoting
the request to a preflight:
Accept,Accept-Language,Content-Language,Content-Type(for the three safelisted values), and a small set of others enumerated by the Fetch Standard. Any header outside this list is what makes a request "non-simple" and triggers a preflight. - CORS-safelisted response header
-
A response header JavaScript may read cross-origin by default:
Cache-Control,Content-Language,Content-Length,Content-Type,Expires,Last-Modified, andPragma. Every other response header returnsnullfromresponse.headers.get()unless the server lists it inAccess-Control-Expose-Headers— even though it is genuinely on the response (visible in the Network tab). - Opaque response
-
The
ResponseJavaScript receives from afetch()made withmode: "no-cors": itstypeis"opaque",statusis0, no headers are readable, and the body is unreachable. The request was still sent and the server still processed it — the browser simply gutted what it hands your code. "Just addno-cors" is a common, wrong fix for a CORS error: it looks like it worked, but your code gets nothing. - Credentialed request
-
A request that carries cookies or HTTP authentication. In
fetch()this requirescredentials: "include"(or"same-origin"). For the browser to let the page read a credentialed cross-origin response, the server must sendAccess-Control-Allow-Credentials: trueand a non-wildcardAccess-Control-Allow-Origin(the exact origin, never*). - The
nullorigin -
The origin serialized as the literal string
null, sent by sandboxediframes (withoutallow-same-origin),data:URLs,file:origins, and redirects from opaque origins. It is not a wildcard and not the same as noOriginheader — a server that allowsnullinAccess-Control-Allow-Originis opting in to all of these, which is almost always a mistake. See Special origins.
Request headers the browser sends
Three headers carry the CORS conversation, and only on the right kinds
of request. A simple request sends just Origin; the other
two appear exclusively on the OPTIONS preflight.
| Header | Sent when | Meaning & gotcha |
|---|---|---|
Origin | Every cross-origin request (simple and preflighted). |
The page's origin (scheme + host + port), never the path. The
server compares this against its allowlist to decide whether to
opt in.
Gotcha: a courtesy header, not authentication — browsers set
it, but curl and any non-browser forge it
freely. |
Access-Control-Request-Method | Preflight only. | The method the real request wants to use, so the server can approve it in advance. |
Access-Control-Request-Headers | Preflight only. |
Comma-list of the non-safelisted headers the real request will
carry.
Gotcha: never echo this verbatim into
Access-Control-Allow-Headers — that reflects a
visitor-supplied value straight into a response header. Match
it against a fixed allowlist instead. |
Response headers the server sends back
The server's opt-in. Each governs a distinct part of the decision;
Access-Control-Allow-Origin is the one that decides a
simple request, while the others are preflight-only or govern what
JavaScript may read off the actual response.
| Header | Relevant in | Meaning & gotcha |
|---|---|---|
Access-Control-Allow-Origin | Simple response, actual response, and preflight response. |
The single origin allowed to read, or the literal
* wildcard. Absent, and the read is blocked.
Gotcha: must match the request's Origin
byte-for-byte — a trailing slash, an explicit default port,
or uppercase scheme all fail to match; and * is
rejected entirely when credentials are involved. |
Access-Control-Allow-Methods | Preflight response only. | The methods the server permits. If the real request's method isn't listed, the browser never sends the real request. |
Access-Control-Allow-Headers | Preflight response only. |
The request headers the server permits, answering
Access-Control-Request-Headers.
Gotcha: hold a fixed allowlist; never reflect the incoming
request-header list back verbatim. |
Access-Control-Allow-Credentials | Preflight response and actual response. |
The single literal token true; its presence lets a
credentialed request read the response. It is never
false — absence is how "no" is said.
Gotcha: combined with Access-Control-Allow-Origin:
*, the browser rejects the credentialed read outright —
a wildcard grant cannot authorise a credentialed request. |
Access-Control-Max-Age | Preflight response only. |
How many seconds the browser may cache the preflight's answer,
so it doesn't re-ask before every request. A performance
header, not a security one.
Gotcha: browsers cap the cached duration per engine (often a
few minutes to ~24 h) regardless of the value sent — a huge
Max-Age is not honoured literally. |
Access-Control-Expose-Headers | Actual response only. |
Which response headers JavaScript may read beyond the
CORS-safelisted set.
Gotcha: the quietest trap in CORS — a header can be fully on
the response (visible in the Network tab) and still return
null from response.headers.get()
unless it is listed here. Not a block, just a silent
withhold. |
Vary | Any response. |
Not CORS-specific, but load-bearing here: it tells shared caches
which request headers select a different response.
Gotcha: when the server returns a different
Access-Control-Allow-Origin per requesting
origin, Vary: Origin is mandatory — omit it and a
CDN may serve origin A's allowed response to origin B. |
Decision flowchart: will this cross-origin request succeed?
The whole mechanism as one walk: same-origin? simple or preflighted? preflight passes? the response grants access? — and, for credentialed requests, an explicit credentials grant. Each branch ends in a clear succeed or blocked outcome. Solid green arrows are paths that succeed; dashed red arrows are paths the browser blocks.
Two simplifications worth naming. The chart treats
the preflight cache as invisible — a cached preflight answer still
"passes" without a new OPTIONS on the wire (see
Caching & Vary). And it treats
Access-Control-Expose-Headers as a separate concern from
whether the read succeeds: a request can pass every branch
here and still have specific response headers return
null unless they are exposed (see
The headers).