HTTP Cheatsheet
Headers Reference
Use this HTTP reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Header syntax rules
Field names are case insensitive (HTTP/2 and /3 require them lowercase on the
wire). Values are ASCII, and non-ASCII needs RFC 8187 encoding
(filename*=UTF-8''na%C3%AFve.pdf).
| Rule | Detail |
|---|---|
| Repeated fields | combine into a comma-separated list, except Set-Cookie |
Set-Cookie | never combined, send one header per cookie |
| Quality values | ;q=0 to ;q=1, three decimals max, default 1 |
X- prefix | deprecated by RFC 6648, do not mint new X- names |
| Trailers | allowed after a chunked body, declared with Trailer |
| Size | no spec limit, but servers cap the block at 4-16 KB in practice |
Request headers by purpose
Routing and identity
| Header | Value | Notes |
|---|---|---|
Host | api.example.com | required in 1.1, becomes :authority |
User-Agent | product tokens | do not gate features on it |
Referer | the originating URL | misspelled since 1996, controlled by Referrer-Policy |
Origin | https://app.example.com | scheme, host, port only, no path, sent on CORS and all POSTs |
Forwarded | for=1.2.3.4;proto=https;host=x | RFC 7239 standard form |
X-Forwarded-For | client, proxy1, proxy2 | de facto, trust only the hops you control |
X-Forwarded-Proto | https | how the app knows the edge terminated TLS |
Content of the request body
| Header | Value |
|---|---|
Content-Type | application/json; charset=utf-8 |
Content-Length | byte count of the body |
Content-Encoding | gzip, if you compressed the request body |
Transfer-Encoding | chunked |
Expect | 100-continue, ask before uploading a large body |
What the client will accept
| Header | Value |
|---|---|
Accept | application/json, text/plain;q=0.9, */*;q=0.1 |
Accept-Encoding | br, gzip, zstd, deflate |
Accept-Language | en-US, en;q=0.9, es;q=0.5 |
Accept-Charset | obsolete, UTF-8 is assumed |
Conditionals and caching
| Header | Value | Pairs with |
|---|---|---|
If-None-Match | "9c1f2a" or * | ETag, yields 304 |
If-Modified-Since | HTTP date | Last-Modified, yields 304 |
If-Match | "9c1f2a" | optimistic locking, yields 412 |
If-Unmodified-Since | HTTP date | yields 412 |
If-Range | etag or date | conditional range request |
Cache-Control | no-cache, max-age=0 | forces revalidation |
Range | bytes=0-1023 | yields 206 |
Auth and state
| Header | Value |
|---|---|
Authorization | Bearer <token>, Basic <base64> |
Proxy-Authorization | credentials for the proxy |
Cookie | a=1; b=2, all cookies in one header |
DNT / Sec-GPC | privacy signals, largely ignored |
Browser-set fetch metadata (Sec- prefixed, unforgeable by scripts)
| Header | Values | Tells the server |
|---|---|---|
Sec-Fetch-Site | same-origin, same-site, cross-site, none | where the request came from |
Sec-Fetch-Mode | navigate, cors, no-cors, same-origin | the fetch mode used |
Sec-Fetch-Dest | document, image, script, empty | what it will be used as |
Sec-Fetch-User | ?1 | a real user gesture triggered it |
Response headers by purpose
Describing the payload
| Header | Value | Notes |
|---|---|---|
Content-Type | text/html; charset=utf-8 | always set charset for text |
Content-Length | bytes | omitted when chunked or compressed on the fly |
Content-Encoding | br, gzip, zstd | must add Vary: Accept-Encoding |
Content-Language | en-US | — |
Content-Disposition | attachment; filename="q3.pdf" | forces a download |
Content-Location | the canonical URL of this payload | — |
Content-Range | bytes 0-1023/146515 | with 206 |
Caching and validation
| Header | Value |
|---|---|
Cache-Control | public, max-age=31536000, immutable |
ETag | "9c1f2a" strong, W/"9c1f2a" weak |
Last-Modified | Tue, 15 Sep 2026 10:04:11 GMT |
Expires | HTTP date, legacy, Cache-Control wins |
Age | seconds this response has sat in a cache |
Vary | Accept-Encoding, Accept-Language |
Control and negotiation
| Header | Value | Sent with |
|---|---|---|
Location | new or created URL | 201 and all 3xx |
Allow | GET, POST, OPTIONS | 405 and OPTIONS |
Retry-After | seconds or an HTTP date | 429, 503, 301 |
WWW-Authenticate | Bearer realm="api", error="invalid_token" | 401 |
Accept-Ranges | bytes or none | advertises range support |
Alt-Svc | h3=":443"; ma=86400 | advertises HTTP/3 |
Upgrade | websocket | 101 |
Server | product token, better omitted or generic | — |
Date | when the response was generated | required |
Link | </style.css>; rel=preload; as=style | preload, pagination rel=next |
Cookies and CORS
| Header | Value |
|---|---|
Set-Cookie | sid=abc; Path=/; Secure; HttpOnly; SameSite=Lax |
Access-Control-Allow-Origin | one origin or * |
Access-Control-Allow-Credentials | true |
Access-Control-Allow-Methods | GET, POST, PATCH |
Access-Control-Allow-Headers | content-type, authorization |
Access-Control-Expose-Headers | which response headers JS may read |
Access-Control-Max-Age | preflight cache seconds |
Conditional requests in practice
# first request, capture the validator curl -sI https://example.com/app.js | grep -i etag # ETag: "9c1f2a" # second request, revalidate curl -sI -H 'if-none-match: "9c1f2a"' https://example.com/app.js # HTTP/1.1 304 Not Modified, no body transferred # optimistic concurrency: only write if nobody else did curl -X PUT https://api.example.com/orders/8814 \ -H 'if-match: "9c1f2a"' -H 'content-type: application/json' \ -d '{"qty":5}' # 412 Precondition Failed if the resource changed since
ETag is the stronger validator and works for any content. Last-Modified has
one-second resolution, so it cannot express two changes in the same second. Send
both when you can, and prefer ETag when they conflict.
Vary, proxies, and the cache poisoning trap
Vary lists the request headers that participate in the cache key. Get it wrong
and a shared cache serves one user's variant to everyone.
| Response varies by | Required Vary |
|---|---|
| compression | Accept-Encoding |
| language | Accept-Language |
| media type | Accept |
| mobile vs desktop markup | User-Agent (very low hit rate, avoid) |
| the logged-in user | do not cache publicly at all, use private |
Any request header that changes the response but is not in Vary (or is not part
of the cache key at the CDN) is a cache poisoning vector. The safe posture is
to keep public responses independent of unkeyed headers, and mark anything
user-specific Cache-Control: private, no-store.
curl -sI https://example.com/ | grep -iE 'vary|cache-control|age|etag' curl -sI -H 'accept-encoding: br' https://example.com/ | grep -i content-encoding curl -s -D - -o /dev/null --compressed https://example.com/