DEV Community

Neural Download
Neural Download

Posted on

15 HTTP Status Codes You'll Actually Hit | Coffee Time

https://www.youtube.com/watch?v=ZAMNET5jEzI

Most HTTP status code references walk the codes numerically — 1xx, 2xx, 3xx, 4xx, 5xx — like a flashcard deck. That ordering teaches the number system but not what actually happens to a request.

Here's a different lens: walk them in the order a request hits them. Connection. Success. Redirect. Client error. Server error. Each transition is a story beat, and once you see them as conversational moves between client and server, the table stops being a memorized list.

Fifteen codes you'll actually hit in production, in journey order.

The Good Path: 100, 200, 204

Three numbers come back when everything works.

100 Continue is the polite knock. Your client wants to upload 50 megabytes. Instead of pushing the bytes blindly, it sends the headers first with Expect: 100-continue and waits. The server checks auth, content length, all of it — and replies 100 only if it intends to accept the body. If the answer was going to be a 401 or a 413, the 50 megabytes never leave your machine.

200 OK is the workhorse. Request landed, server did the work, here's the body.

204 No Content is "I did it, nothing to say." Common after a DELETE — there's no representation of nothing. The spec actually forbids a body on a 204; sending one is a protocol violation.

The Redirect Trap: 301, 304, 308

Sometimes the server doesn't answer — it points somewhere else.

301 Moved Permanently sounds clean. It is not. POST a form to a URL that returns 301, and your browser will silently change the POST to a GET, drop the body, and follow the new URL with no payload. The spec literally codifies this: "For historical reasons, a user agent MAY change the request method from POST to GET for the subsequent request."

That bug broke form submissions on the web for twenty years. Browsers did it so consistently that codifying the wrong behavior was easier than fixing it.

308 Permanent Redirect is the fix. Same meaning as 301, except the method is preserved. Your POST stays a POST. Your body comes with you. 308 exists for one reason: to undo 301's mistake.

304 Not Modified is the cache validator. Your browser sends a hash (If-None-Match: "abc123"), the server compares it against the current ETag, and if nothing's changed it replies 304 with header section only. No body. The smallest useful response in HTTP — every revisit to a cached asset costs ~200 bytes of headers instead of the full payload.

Your Fault: 400, 401, 403, 404

Four ways the server tells you the problem is on your side.

400 Bad Request — your JSON has a trailing comma, your headers are malformed, the server couldn't parse what you sent.

401 Unauthorized is misnamed. It actually means unauthenticated — the server doesn't know who you are. And here's the part most APIs get wrong: a 401 MUST send back a WWW-Authenticate header. That header tells the client how to retry — which auth scheme, which realm. Without it, the client has no path forward. It's spec-mandated, not optional. If your API returns 401 with no challenge header, you're violating HTTP.

403 Forbidden means the server knows exactly who you are. You just can't have this. Sending the same credentials again won't help.

404 Not Found — and here's where it gets interesting. 404 is allowed to lie.

Try to hit a private GitHub repo while logged in. By the rules, GitHub should return 403 — you exist, you're authenticated, you don't have access. Instead, GitHub returns 404. Same response as if the repo didn't exist at all.

The spec explicitly blesses this. RFC 9110: "An origin server that wishes to 'hide' the current existence of a forbidden target resource MAY instead respond with a status code of 404 (Not Found)."

The lie is the feature. If GitHub returned 403, an attacker could probe URLs and learn which private repos exist by which ones say "you can't have this" versus "no such thing." 404 collapses both signals into one.

The Personality Codes: 418, 429, 451

Three more 4xx codes, written by humans.

418 I'm a teapot. April 1, 1998 — Larry Masinter at Xerox wrote a joke RFC for a coffee pot control protocol. 418 meant you tried to brew coffee in a teapot. Pure gag.

Twenty years later, the IETF tried to clean it up and remove 418 from the registry. The internet revolted. Save 418. Forks of major web frameworks added 418 handlers. The IETF reversed course. RFC 9110 §15.5.19 codifies the truce: "the definition of an application-specific 418 status code... has been deployed as a joke often enough for the code to be unusable for any future use. Therefore, the 418 status code is reserved in the IANA HTTP Status Code Registry." The joke became permanent.

429 Too Many Requests is the only 4xx that tells you when to come back. Headers can include Retry-After — five seconds, an exact timestamp, whatever. Every other 4xx is a flat rejection; 429 is a contract.

451 Unavailable For Legal Reasons — the number is a Fahrenheit 451 reference. Bradbury's novel about burning books. Tim Bray proposed it in 2013, ratified as RFC 7725 in 2016. When a court or government has demanded the server block this content, 451 is the response. It can include a Link header with rel="blocked-by" pointing to the entity demanding the block. Censorship made machine-readable.

Their Fault: 500, 502, 503, 504

Now it's the server's fault — but not always the same server.

In production, your traffic doesn't hit one box. It hits a load balancer. The load balancer talks to a backend behind it. The 5xx family points fingers at four different places along that chain.

  • 500 Internal Server Error — something broke inside the backend. Uncaught exception, null pointer. The catch-all.
  • 502 Bad Gateway — the load balancer talked to the backend, the backend replied, and the reply was garbage. Malformed bytes. Connection reset mid-response. Upstream gave junk.
  • 503 Service Unavailablethis server, the one you're talking to, is overloaded or in maintenance. Often comes with Retry-After.
  • 504 Gateway Timeout — the load balancer asked the backend a question and got silence. No reply within the timeout window.

Mnemonic: 500 = backend crashed, 502 = backend replied with garbage, 503 = this box is overloaded, 504 = backend went silent. Four codes, four different broken links.

The Map

The whole point of journey ordering: when you see a code in production, you can place it.

  • 1xx — handshake
  • 2xx — request landed
  • 3xx — request rerouted
  • 4xx — request rejected (you)
  • 5xx — request died inside (them)

Next time you hit a code, don't ask what does this number mean. Ask where on the journey did this request die. The answer is already in the number.

Top comments (0)