Status codes: read the class first, then the value
| Class | Meaning | Common values |
|---|---|---|
| 1xx | Informational | 100 Continue, 101 Switching Protocols |
| 2xx | Success | 200, 201 Created, 204 No Content |
| 3xx | Redirection | 301 permanent, 302 temporary, 304 not modified, 307/308 method-preserving |
| 4xx | Client error | 400, 401 unauthorised, 403 forbidden, 404, 409 conflict, 429 too many requests |
| 5xx | Server error | 500, 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
| Header | Purpose | Typical value |
|---|---|---|
| Cache-Control | The main policy | no-store / no-cache / max-age=3600 / immutable |
| ETag | Fingerprint for revalidation | "abc123" (with If-None-Match) |
| Last-Modified | Modification time | Used with If-Modified-Since |
| Vary | Which request headers split the cache | Accept-Encoding, Accept-Language |
A strategy you can apply directly
- Hashed build artefacts: public, max-age=31536000, immutable — a new filename means a new version;
- HTML pages: max-age=0, must-revalidate so a deploy takes effect immediately;
- API responses: no-store for private data; short max-age plus ETag for shareable public data;
- 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.
- 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.
- Redirect permanence: use permanent redirects sparingly — once cached by browsers they are hard to undo; temporary redirects suit canaries and maintenance.
- 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.
- 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.
- 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.