Practical guide 6 min read

HTTP Status Codes in Practice: The Ones That Matter and the Decisions Behind Them

A working guide to status codes — which codes your API should actually return, redirect method preservation, and how to read them in monitoring.

HTTP status codes are a contract older than most of the companies using them. Most APIs survive on a handful of codes — but picking the right one at each decision point changes how clients, caches, and monitoring systems behave. Here is the working subset, with the decisions attached.

The success codes: 200, 201, 204

  • 200 OK — the default success. Returns a body with the result.
  • 201 Created — a POST that created a resource. Return it with a Location header pointing at the new resource; clients that follow links get correct behavior for free.
  • 204 No Content — success with nothing to say: DELETE confirmations, PUT updates where the client already knows the state. Saves an empty-body round trip. Two cautions: 204 must not carry a body, and await res.json() on a 204 throws — client code should branch on status.

Redirects: the method-preservation matrix

The redirect codes differ on one axis that breaks APIs when ignored: whether the request method survives.

CodeMeaningMethod preserved?Cached?
301Moved permanentlyOften rewritten POST→GETYes, aggressively
302Found (temporary)Often rewritten POST→GETNo
307Temporary redirectStrictly preservedNo
308Permanent redirectStrictly preservedYes

For API versioning (/v1/…/v2/…) use 308 — a rewritten POST is a corrupted API call. For page moves, 301 is fine and its caching is a feature. When migrating, check the redirect chain with a URL parser — accidental double-redirects multiply latency.

The 4xx family: say what you mean

  • 400 Bad Request — malformed syntax. If the client can fix nothing by retrying unchanged, 400 is right.
  • 401 vs 403 — 401 = “who are you?” (unauthenticated; include WWW-Authenticate), 403 = “I know who you are; no” (unauthorized). Confusing them turns your error logs into noise.
  • 404 vs 410 — 404 for missing and maybe-temporarily-missing; 410 Gone tells caches and crawlers “permanently removed, stop asking” — useful for de-indexing expired content faster than 404.
  • 429 Too Many Requests — the rate-limit code, always with Retry-After. Clients that respect it are doing you a favor; make respecting it easy.

The 5xx family and what monitoring should do with them

500 is the generic crash, 502/504 mean an upstream/gateway failed (classic proxy-speak for “the app behind me is down or slow”), and 503 means deliberate unavailability (maintenance or overload — include Retry-After). The monitoring division of labor: 5xx spikes page the on-call; 4xx spikes get investigated, not paged — they usually mean a broken client release, an abusive crawler, or an expired token storm.

Status codes are also a caching contract

Codes are how caches decide what to keep: 200 with fresh headers is cacheable, 301 is cached aggressively, 404/410 are cacheable by many CDNs (a cached 404 can outlive the fix — purge after deployment), and 5xx must never be cached. When “we fixed it but some users still see the old response”, the status code and its cacheability are the first suspects. The full HTTP status reference includes the cacheability and method rules for every code, so the contract is one lookup away.