HTTP Headers Quick Reference
The request and response headers that control caching, content, auth, CORS, and security — what each one does and sane values.
Headers are the metadata of every HTTP exchange: case-insensitive Name: value lines sent ahead of the body that tell the other side how to read it, cache it, secure it and route it. Request headers describe the client and what it wants; response headers describe the payload and the rules attached to it.
Many bugs that look like application bugs (stale pages, blocked fetches, lost cookies, wrong client IPs) are really one missing header or one wrong value. The fastest diagnosis is reading the raw headers with curl -v or the browser’s network panel.
Content & negotiation
- Content-Type
- Body’s media type:
application/json,text/html; charset=utf-8 - Accept
- What the client wants back:
Accept: application/json - Content-Encoding
gzip/br— compression applied to the body- Accept-Encoding
- compressions the client can decode, e.g.
gzip, br, zstd; the server picks one - Content-Disposition
attachment; filename="a.pdf"— force download- Content-Length
- body size in bytes; absent when HTTP/1.1 streams with
Transfer-Encoding: chunked - Vary
- request headers that change the response, e.g.
Vary: Accept-Encoding; caches key on them - Accept-Language
- the user’s languages with weights:
en-US,en;q=0.9
Caching
- Cache-Control
max-age=3600·no-store(never cache) ·private/public- no-cache ≠ no-store
no-cachemay store but must revalidate before reuse; onlyno-storeforbids storing- Immutable assets
Cache-Control: public, max-age=31536000, immutable(hashed filenames)- s-maxage
- a max-age for shared caches such as CDNs only; it overrides
max-agethere - stale-while-revalidate
- serve a stale copy for that many seconds after expiry while refetching in the background
- ETag / If-None-Match
- Validator; server replies
304 Not Modifiedif unchanged - Last-Modified
- Timestamp validator; paired with
If-Modified-Since - Age
- seconds the response sat in a cache — a nonzero value means a cache served it
- Expires
- legacy absolute expiry date, ignored when
Cache-Control: max-ageis present - CDN-Cache-Control
- cache rules for CDNs only (RFC 9213), so browsers and CDNs can get different TTLs
Conditional & range requests
- If-Match
- optimistic concurrency: write only if the ETag still matches, else
412 Precondition Failed - Range
bytes=0-1023asks for part of a resource; answered with206andContent-Range- Accept-Ranges
bytesmeans range requests work — resumable downloads, video seeking
Auth & cookies
- Authorization
Bearer <token>·Basic <base64>- WWW-Authenticate
- sent with
401to name the auth scheme, e.g.Bearer realm="api" - Set-Cookie
- Add
HttpOnly; Secure; SameSite=Lax— the safe default - SameSite=None
- needed for cross-site cookies (embeds, third-party SSO); rejected without
Secure - __Host-session
- name prefix: browsers enforce
Secure,Path=/and noDomainattribute - Max-Age=3600
- cookie lifetime in seconds, winning over
Expires; with neither it is a session cookie - Cookie
- what the browser sends back:
name=valuepairs only, never the attributes
CORS (Access-Control-…)
- Origin
- scheme, host and port of the calling page; the server makes its CORS decision from it
- …-Allow-Origin
- CORS: which origins may read the response. Avoid blind
*with credentials. - …-Allow-Methods
- Allowed methods on the resource (preflight response)
- …-Allow-Headers
- request headers the real request may carry, e.g.
Authorization, Content-Type - …-Allow-Credentials
truelets cookies through; then Allow-Origin must name the exact origin, never*- …-Expose-Headers
- extra response headers JavaScript may read, such as
X-Request-ID - …-Max-Age
- seconds a browser may cache the preflight result; Chrome caps it at 2 hours
- OPTIONS preflight
- sent before non-simple requests, carrying
Access-Control-Request-Methodand-Headers
Security headers
- Strict-Transport-Security
max-age=63072000; includeSubDomains— force HTTPS (HSTS)- HSTS preload
- add
preloadand submit the domain to browsers’ list: HTTPS even on a first visit; hard to undo - Content-Security-Policy
- allowlist where scripts, styles and frames may load from — the strongest defense against XSS
- CSP Report-Only
Content-Security-Policy-Report-Onlyreports violations without blocking — roll out safely- X-Content-Type-Options
nosniff— stop MIME sniffing- X-Frame-Options
DENY— prevent clickjacking (or use CSP frame-ancestors)- Referrer-Policy
strict-origin-when-cross-origin— limit referrer leakage- Permissions-Policy
- switch off browser features you don’t use:
camera=(), geolocation=() - COOP
Cross-Origin-Opener-Policyset tosame-origincuts ties to cross-origin popups and openers- COEP · CORP
- embedder and resource policies; with COOP they enable cross-origin isolation
- X-XSS-Protection
- obsolete — modern browsers ignore it; send
0or omit it and rely on CSP
Proxies, tracing & APIs
- Host
- which site the request is for — required in HTTP/1.1,
:authorityin HTTP/2 - X-Forwarded-For
- client IP chain added by proxies; trust only entries your own proxies appended
- X-Forwarded-Proto
- the original scheme — how an app behind TLS termination knows the user used HTTPS
- Forwarded
- the standard form (RFC 7239):
for=203.0.113.7;proto=https - Location
- where to go next: the target of a
3xxredirect, or the new resource after201 - Retry-After
- seconds (or a date) to wait, sent with
429or503; clients should honor it - X-Request-ID
- a correlation ID echoed into every hop’s logs — quote it in bug reports
- traceparent
- W3C Trace Context: trace and parent span IDs that stitch distributed traces together
- Idempotency-Key
- client-chosen key so a retried POST takes effect once (Stripe-style APIs)
- X-RateLimit-Remaining
- a common convention for quota left; an IETF draft standardizes
RateLimitheaders - Server-Timing
- backend timings shown in browser dev tools:
db;dur=53, app;dur=47 - Link
</items?page=2>; rel="next"for pagination, orrel=preloadhints- Alt-Svc
- advertises HTTP/3 on another protocol or port, e.g.
h3=":443"
Recipes
Cache headers for a typical web app
# Hashed static assets (app.3f9a1c.js)
Cache-Control: public, max-age=31536000, immutable
# HTML pages: always revalidate, cheap 304s via the ETag
Cache-Control: no-cache
ETag: "v42"
# Personalized API responses
Cache-Control: no-storeFingerprinted file names make year-long caching safe because new content gets a new URL; HTML must revalidate so users pick up the new asset links right after a deploy.
Check a site’s security headers from the terminal
curl -sI https://example.com | grep -iE 'strict-transport|content-security|x-content-type|x-frame|referrer-policy|permissions-policy'-I fetches headers only and grep -i ignores case, since header names are case-insensitive. Any header missing from the output is one to add.
A correct CORS preflight exchange
OPTIONS /api/orders HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type, authorization
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: content-type, authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 600
Vary: OriginWith credentials the server must echo the exact origin, never *, and Vary: Origin stops a cache serving one origin’s answer to another.
Revalidate instead of re-downloading
curl -sI https://example.com/data.json | grep -i etag
# etag: "abc123"
curl -s -o /dev/null -w '%{http_code}\n' -H 'If-None-Match: "abc123"' https://example.com/data.json
# 304A 304 Not Modified has no body, so checking freshness costs one round trip instead of the whole payload.
A hardened session cookie
Set-Cookie: __Host-session=8f3a2c; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=86400HttpOnly hides it from JavaScript, Secure keeps it on HTTPS, SameSite=Lax blocks most CSRF, and the __Host- prefix pins it to this exact host.
Questions about HTTP Headers Cheat Sheet
What is the difference between Cache-Control no-cache and no-store?
no-cache does not mean do not cache. It lets browsers and CDNs store the response but requires them to revalidate with the server before every reuse, typically with If-None-Match or If-Modified-Since, so an unchanged resource costs only a quick 304. no-store forbids storing the response anywhere, which is what you want for sensitive data such as account pages or one-time tokens. For HTML that changes with each deploy, no-cache plus an ETag gives fresh content cheaply; for hashed static assets, use a long max-age with immutable instead.
Why is my CORS request failing?
Start with the browser console and the preflight request. If the request uses a method other than GET, HEAD or POST, a custom header such as Authorization, or a JSON content type, the browser first sends an OPTIONS preflight, and the server must answer it with a 2xx status and the right Access-Control-Allow headers. Common causes are a server that returns 404 or 401 to OPTIONS, a missing Allow-Headers entry, or credentials sent while Allow-Origin is the wildcard. CORS is enforced by browsers only, so curl and server-to-server calls succeed regardless.
Which security headers should every website send?
A solid baseline is Strict-Transport-Security with a long max-age so browsers only use HTTPS; a Content-Security-Policy that restricts where scripts, styles and frames can load from, the strongest protection against cross-site scripting; X-Content-Type-Options set to nosniff; frame protection through CSP frame-ancestors or X-Frame-Options; a Referrer-Policy such as strict-origin-when-cross-origin; and a Permissions-Policy that disables features you do not use. X-XSS-Protection is obsolete and can be dropped. Roll out CSP in report-only mode first to see what it would break.
What is the difference between ETag and Last-Modified?
Both are validators that let a cache ask whether its copy is still current. Last-Modified is a timestamp with one-second resolution, checked with If-Modified-Since, so two changes within the same second, or a file whose timestamp changes without new content, can confuse it. An ETag is an opaque version identifier, often a content hash, checked with If-None-Match, and it is exact. A weak ETag, prefixed with W/, means equivalent rather than byte-identical. Servers often send both, and clients use the ETag when it is present.