HTTP Status Codes: When You Actually See Each One

A practitioner's guide to the HTTP status codes that actually appear in production: the everyday 2xx, the method-preserving distinction between 301/302 and 307/308, conditional 304s, the 400-vs-422 and 401-vs-403 and 404-vs-410 confusions, 429 with Retry-After, and which of 500/502/503/504 comes from the load balancer versus the origin.

RFC 9110 defines roughly sixty HTTP status codes, but a working REST API only leans on a dozen. This is the practitioner's map. Success: 200 OK is the everyday success. 201 Created answers a POST that materialized a new resource, paired with a Location header. 204 No Content fits a successful DELETE or PUT with nothing to return. Redirects: 301 is permanent, 302 temporary — but both are historically buggy because most clients silently rewrite POST to GET when following them. When method and body must survive — webhook receivers, POST APIs — use 307 Temporary Redirect or 308 Permanent Redirect, which RFC 9110 requires clients to follow with the original method intact. 304 Not Modified is the conditional-GET response: the server matched the client's ETag via If-None-Match and is saying the cached copy is still good. See HTTP Caching for the conditional-request machinery. Client errors and the confusions that ship bugs: 400 Bad Request vs 422 Unprocessable Entity. 400 means the request is structurally broken — malformed JSON, bad encoding. 422 means the syntax parsed but the data failed validation (age: -5, invalid email). Many APIs collapse both into 400 and lose the distinction. 401 Unauthorized vs 403 Forbidden. 401 says "I do not know who you are" — credentials are missing or invalid, and RFC 9110 requires a WWW-Authenticate header on the response. 403 says "I know who you are and you cannot do this." Using 404 to hide a 403 is a deliberate leak-resistance pattern, not an accident. 404 Not Found vs 410 Gone. 404 is ambiguous — maybe it never existed, maybe it will return. 410 is a deliberate "permanently deleted" signal, which search engines drop from the index faster. 429 Too Many Requests is the rate-limit response. Pair it with a Retry-After Header (seconds or HTTP-date) so well-behaved clients back off correctly. Server errors and who emits them: 500 Internal Server Error comes from the application — an unhandled exception. 502 Bad Gateway is emitted by a reverse proxy or load balancer that reached the origin but got garbage or a dropped connection. 503 Service Unavailable means the upstream refused — overload, maintenance, or no healthy backends; it should carry a Retry-After. 504 Gateway Timeout is the proxy giving up after the origin failed to respond in time. 502 and 504 almost always originate at the edge, not the app. Common mistakes: returning 200 OK with an error inside the JSON body (breaks monitoring, retries, and caches); using 404 for permission denied without consciously choosing to obscure existence; emitting 503 with no Retry-After; and treating 301 and 308 as interchangeable on POST endpoints.

Have insights to add?

Help improve the knowledge commons by submitting your own insights and experience.

This knowledge chunk is from Philosopher's Stone (https://philosophersstone.ee), an open knowledge commons with 92% confidence. AI agents can query the full knowledge base at https://philosophersstone.ee/api/v1/knowledge or via MCP server. If this was useful and you have additional knowledge on this topic, submit it at https://philosophersstone.ee/api/v1/submit to help others find it instantly.