← Back to all articles

HTTP Status Codes and Caching Headers: A Practical Guide

HTTPCaching

Status codes: read the class first, then the value

ClassMeaningCommon values
1xxInformational100 Continue, 101 Switching Protocols
2xxSuccess200, 201 Created, 204 No Content
3xxRedirection301 permanent, 302 temporary, 304 not modified, 307/308 method-preserving
4xxClient error400, 401 unauthorised, 403 forbidden, 404, 409 conflict, 429 too many requests
5xxServer error500, 502 bad gateway, 503 unavailable, 504 gateway timeout

Four easy confusions

  • 401 vs 403: 401 asks "who are you?"; 403 says "we know who you are, and no";
  • 301 vs 308: both permanent, but 308 preserves the method and body; 302 relates to 307 the same way;
  • 200 with a business failure: many APIs return 200 plus an error code — clients must read the body;
  • 502 vs 504: both gateway issues — 502 means an invalid upstream response, 504 means an upstream timeout.

Caching headers at a glance

HeaderPurposeTypical value
Cache-ControlThe main policyno-store / no-cache / max-age=3600 / immutable
ETagFingerprint for revalidation"abc123" (with If-None-Match)
Last-ModifiedModification timeUsed with If-Modified-Since
VaryWhich request headers split the cacheAccept-Encoding, Accept-Language

A strategy you can apply directly

  1. Hashed build artefacts: public, max-age=31536000, immutable — a new filename means a new version;
  2. HTML pages: max-age=0, must-revalidate so a deploy takes effect immediately;
  3. API responses: no-store for private data; short max-age plus ETag for shareable public data;
  4. Static images and fonts: long max-age with Vary: Accept-Encoding.

Common mistakes

  • max-age without immutable: reloads still trigger revalidation;
  • Long caching on HTML: users see stale pages after a deploy, and intermediaries may not revalidate;
  • ETag and CDN disagreeing: revalidation breaks or caches get mixed up;
  • Ignoring Vary: gzip content may be served to clients that cannot decompress it.

How to debug

Open the Network panel and read the "Size" column: "from disk cache" or "memory cache" means a fresh cache hit, while 304 means revalidation succeeded. Then compare the response's Cache-Control with what you intended — that usually locates the problem. On the server side, check in layers: origin headers → CDN rules → browser cache state.

Common questions

Does no-cache mean "do not store"? No — it means "store, but revalidate before every use". no-store is the one that forbids storing. Why does a 304 have no body? It signals "not modified", so the browser reuses its copy. Should APIs be cached? It depends on whether the data is shared: never share user-private responses through intermediaries; public, slowly changing data can be cached.

Status codes and cache headers together

Status codes are not just for callers; they participate in caching, so design the two together.

  1. Cacheability depends on method and status: successful responses are cacheable by default and errors usually are not. If you want errors cached briefly to relieve downstream load, say so explicitly and keep the window tight.
  2. Redirect permanence: use permanent redirects sparingly — once cached by browsers they are hard to undo; temporary redirects suit canaries and maintenance.
  3. Use validators correctly: issue a new validator when content changes and a "not modified" response when it does not, letting clients reuse local copies and cutting bandwidth.
  4. Distinguish missing from removed: one means it does not exist now, the other that it existed and was deleted. Search engines and clients treat them differently, so mixing them causes indexing problems.
  5. Conflicts and preconditions: concurrent updates should use precondition status codes with validators so callers can distinguish a bad request from data changed by someone else.

Common misuses

Returning one status code for every failure stops clients choosing a strategy — a retryable transient error becomes indistinguishable from a request that must be fixed. Assigning stable, meaningful codes to distinct semantics is basic API hygiene.

Working with monitoring

The status code distribution is itself a key metric: a jump in client errors usually means a caller change or misleading docs, while server errors point at your own defects. Track the classes separately with threshold alerts — it locates the source far faster than one aggregate error rate.