awesome-everything RU
↑ Back to the climb

Networking & Protocols

HTTP headers, caching, and CORS

Crux The HTTP header layer — methods, status codes, Cache-Control, ETags, cookies, CORS — is where most operational bugs live and most security wins or losses happen.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at middle altitude — in the sky
◷ 16 min

The HTTP version determines how bytes travel. The headers determine what happens to them — whether they’re cached, who can read them cross-origin, how long cookies live, and whether the browser enforces HTTPS forever. Getting headers wrong is the most common source of production security bugs.

Methods and idempotency

HTTP defines verbs (methods) for what the client wants: GET (read), POST (create or generic non-idempotent), PUT (idempotent replace), DELETE (idempotent remove), PATCH (partial update), HEAD (metadata-only GET), OPTIONS (capabilities check), CONNECT (tunnel for proxies).

Idempotency (RFC 9110 § 9.2.2): a method is idempotent if running it multiple times produces the same result as running it once. GET, HEAD, PUT, DELETE, and OPTIONS MUST be safe to retry automatically after timeout because the result is the same regardless of repetition. POST and PATCH are not idempotent — auto-retrying them can create duplicate orders, duplicate payments, or duplicate records.

The practical implication: HTTP clients (browsers, load balancers, CDN edge nodes) may retry idempotent methods transparently on network error. POST /orders should never be auto-retried without an Idempotency-Key header or application-level deduplication.

Status codes

Status codes follow RFC 9110:

  • 1xx informational: 100 Continue (client can send body), 101 Switching Protocols (WebSocket upgrade), 103 Early Hints (preload critical resources before 200 arrives).
  • 2xx success: 200 OK, 201 Created (POST that created a resource), 204 No Content (DELETE success), 206 Partial Content (range request fulfilled).
  • 3xx redirect: 301 Moved Permanently (cached by browsers forever), 302/307 Temporary Redirect (not cached), 304 Not Modified (cache validation hit — empty body).
  • 4xx client error: 400 Bad Request, 401 Unauthorized (requires authentication), 403 Forbidden (authenticated but not allowed), 404 Not Found, 409 Conflict, 422 Unprocessable Entity (validation error), 429 Too Many Requests.
  • 5xx server error: 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout.

Common mistake: returning 200 OK with { "error": "not found" } in the JSON body. This bypasses HTTP’s machinery — CDNs cache the 200, retry logic doesn’t trigger, browser console doesn’t show an error. Use the correct status code.

Caching: Cache-Control and ETags

The browser, CDN edges, and reverse proxies all cache responses. Cache-Control is the directive:

  • max-age=3600 — cache for 3600 seconds; no revalidation needed until stale.
  • no-cache — store the response, but revalidate with the origin before serving (sends If-None-Match). Good for data that changes frequently but can be served from cache if still fresh.
  • no-storedo not store at all — not in browser cache, not at CDN. Use for highly sensitive data (banking statements, account pages, auth tokens).
  • private — only the browser may cache this; CDN/proxy must not. Use for user-specific data.
  • public — any cache may store this. CDNs need this (or no Cache-Control) to cache at all.
  • stale-while-revalidate=60 — serve the stale copy immediately, revalidate in background.

ETags enable conditional requests: the server sends ETag: "v2" with a response. When the browser re-requests the resource, it includes If-None-Match: "v2". If the ETag still matches, the server returns 304 Not Modified with an empty body — saving bandwidth. If-Modified-Since is the date-based equivalent; ETags are preferred because they are more precise.

Order the steps

Order common cache-related headers in their typical lifecycle:

  1. 1 Response carries ETag and Cache-Control: max-age=3600
  2. 2 Browser caches the response for 3600 seconds
  3. 3 After 3600s the cache entry becomes stale
  4. 4 Next request: browser sends If-None-Match with the stored ETag
  5. 5 Server returns 304 Not Modified (empty body) if ETag still matches
  6. 6 Or 200 OK with new body + new ETag if content changed

CORS — Cross-Origin Resource Sharing

Browsers enforce the same-origin policy: a script on a.example.com cannot read responses from b.example.com by default. CORS opts in:

  • Server replies with Access-Control-Allow-Origin: https://a.example.com to permit access from that origin.
  • Wildcard * permits any origin but cannot be combined with credentials — the browser refuses to send cookies or Authorization headers to a wildcard origin.
  • Preflight requests (OPTIONS): for non-simple methods (PUT, DELETE, custom headers), the browser first sends an OPTIONS request to check if the server allows the cross-origin access. On success, the real request proceeds.
  • Access-Control-Allow-Credentials: true plus a non-wildcard explicit origin lets cross-origin requests carry cookies and Auth headers.

Misconfigured CORS pitfall: Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true is a security hole. The browser prevents this combination — but some proxy configurations or server frameworks allow it via bugs, enabling confused-deputy attacks where a malicious cross-origin page reads authenticated user data.

Cookies: security flags

Cookies persist client state across requests. Server sets via:

Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=3600; Path=/
  • HttpOnly — blocks JavaScript from reading the cookie (document.cookie cannot see it). This is the primary XSS mitigation for session tokens.
  • Secure — only send on HTTPS. A session cookie without Secure may be sent on HTTP fallback connections.
  • SameSite=Strict — never send on cross-site requests (including top-level navigations). SameSite=Lax — send on safe cross-site navigations (following a link) but not on POST cross-site. SameSite=None; Secure — send on all cross-site requests (for legitimate third-party embedded content — requires Secure).

Modern browsers default to SameSite=Lax if the directive is absent — this breaks many legacy third-party cookie flows. The shift is intentional: browsers are tightening privacy defaults.

Session token security: rotate tokens on privilege escalation (login), use HttpOnly + Secure, and add CSRF protection (SameSite + a separate CSRF token) for state-changing requests.

Security headers

Common response headers that harden the browser’s behaviour:

  • Strict-Transport-Security (HSTS): tells browsers to always use HTTPS for this domain, for max-age seconds, and optionally includeSubDomains. Browsers remember this and refuse HTTP connections without even contacting the server.
  • Content-Security-Policy (CSP): tells browsers which sources are allowed to load scripts, styles, fonts, frames. script-src 'self' blocks inline scripts and external scripts not from the same origin — a powerful XSS mitigation.
  • X-Content-Type-Options: nosniff — prevents browsers from MIME-sniffing responses away from the declared Content-Type.
  • Referrer-Policy: strict-origin-when-cross-origin — limits how much URL information leaks in the Referer header on cross-origin requests.

Content negotiation and range requests

Content negotiation: clients tell the server what they prefer via Accept headers. Accept: application/json, text/html;q=0.9 means JSON preferred, HTML acceptable. Accept-Encoding: br, gzip lists supported compressions. The Vary response header tells caches which request headers affect the response — without Vary: Accept-Encoding, a cache may serve a gzip-compressed body to a client that can’t decompress it.

Range requests: Range: bytes=500-999 asks for a byte range; server replies with 206 Partial Content. Used for HTTP-based video streaming (browsers fetch the file in chunks as the user watches) and resume-after-disconnect. Servers must return Accept-Ranges: bytes to advertise support.

Quiz

Which HTTP method is required to be idempotent per RFC 9110?

Quiz

What does Cache-Control: no-store mean to a browser and CDN?

Trace it
1/4

Trace a cross-origin API call from a browser application.

1
Step 1 of 4
JavaScript on https://app.example.com calls fetch('https://api.other.com/data') with credentials. What does the browser do first?
2
Locked
The OPTIONS preflight arrives at the server. What headers must the server return for the actual request to proceed?
3
Locked
Preflight succeeds. Browser sends the actual GET. API returns data with ACAO: https://app.example.com and ACAC: true. Can JavaScript read the response?
4
Locked
A developer changes the server to return Access-Control-Allow-Origin: * to simplify. What breaks?
Recall before you leave
  1. 01
    When should you set Cache-Control: no-store vs no-cache?
  2. 02
    Why is CORS not a security boundary for APIs?
  3. 03
    What does SameSite=Lax do and why is it the browser default?
Recap

HTTP methods define the intended operation — idempotent methods (GET, HEAD, PUT, DELETE) may be auto-retried; non-idempotent (POST, PATCH) must not be without idempotency keys. Status codes are semantic signals that HTTP infrastructure (CDNs, retry logic, browser UI) relies on — using 200 for errors breaks the machinery. Cache-Control directives (max-age, no-cache, no-store, private) control where and how long responses are stored; ETags enable cheap revalidation via 304 Not Modified. CORS is a browser-enforced same-origin policy opt-in — not a server-side access-control mechanism. Cookies need HttpOnly (blocks XSS), Secure (HTTPS-only), and SameSite=Lax or Strict (CSRF mitigation). HSTS and CSP harden the browser’s security posture at the header level.

Connected lessons
Continue the climb ↑HTTP streaming, SSE, WebSockets, and gRPC
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.