DEV Community

Sarfaraz
Sarfaraz

Posted on Originally published at samtoolkit.com

The HTTP Status Code You're Returning Is Probably Wrong (And Nobody Notices)

Quick test: what's the difference between 401 and 403? What about 502 vs 503? If you had to pause even for a second, you're not alone — and you're also not off the hook, because the client code (or the poor human) on the other end of your API is making decisions based on that number.

Status codes and headers are one of those "I'll just wing it" corners of web development. Everyone's shipped a 500 for a validation error at least once. This post is a quick, practical pass through the ones that actually matter, the ones people mix up, and the headers most APIs quietly get wrong.

Why this is worth five minutes

A status code is a contract. It tells the caller what kind of thing just happened without them reading your response body:

  • Should I retry this request?
  • Is this my fault or the server's?
  • Should I refresh my auth token?
  • Is this safe to cache?

Get the code wrong, and you break that contract. Clients retry things they shouldn't, cache things they can't, or silently swallow errors because your 200 said everything was fine.

The mix-ups that happen constantly

401 vs 403401 Unauthorized actually means "I don't know who you are" (no or invalid credentials). 403 Forbidden means "I know exactly who you are, and the answer is no." Mixing these up gives users the wrong recovery path — one says "log in again," the other says "stop asking."

404 vs 410404 says "not found," which invites retries and crawlers to keep checking. 410 Gone says "this existed and is never coming back." If you deleted a resource on purpose, 410 is the more honest answer and it tells search engines to stop indexing it.

500 vs 502 vs 503500 is your app's own bug. 502 Bad Gateway means you're a proxy/gateway and the upstream service sent garbage. 503 Service Unavailable means you're intentionally down (maintenance, overload) and should usually come with a Retry-After header. These three get used interchangeably in a lot of codebases, and it makes on-call debugging harder than it needs to be.

PUT vs PATCH response codes — a successful PUT that creates a new resource should return 201 Created; one that updates an existing one should return 200 OK (or 204 No Content if you're not returning a body). A lot of APIs just return 200 for everything, which quietly discards useful information.

The headers people forget exist

  • Cache-Control — the single header most responsible for either a snappy site or a stale one. no-store, no-cache, and max-age=0 all mean subtly different things.
  • ETag combined with If-None-Match — lets clients skip re-downloading unchanged resources. Massively underused outside of CDNs.
  • Retry-After — pairs with 429 and 503 to tell clients exactly when to come back, instead of making them guess with exponential backoff.
  • Content-Security-Policy — not just a security checkbox; misconfiguring it is one of the more common reasons a perfectly good frontend "randomly" stops loading fonts or scripts.
  • Vary — if your response changes based on a header like Accept-Encoding or Authorization, and you're not setting Vary, your CDN or browser cache can serve the wrong version to the wrong user.

Where this actually bites you

  • A retry storm because your API returned 500 instead of 429 for rate limiting, so clients treated a "slow down" as a random failure and hammered you harder.
  • A broken cache because a response with private data went out with Cache-Control: public.
  • A frontend team that built retry logic around 503 because your team returned it for something that was actually a permanent 410.

None of these are exotic edge cases — they're the ordinary Tuesday bugs that come from treating status codes and headers as decoration instead of protocol.

A reference beats a memory test

Honestly, memorizing all ~60 status codes and every header's exact semantics isn't a good use of anyone's time. I put together a quick lookup tool that covers status codes (grouped by category, with the "what it actually means" and "when to use it" spelled out) and common request/response headers, so you can check instead of guess mid-code-review:

👉 HTTP Status Code & Header Reference

It's part of samtoolkit.com — a set of small, client-side developer tools (JWT decoder, .gitignore generator, env validator, webhook bin, and a few others). Nothing gets uploaded anywhere; everything runs in your browser.

If you've got a favorite status-code horror story — a 200 that lied to you, a 418 used unironically in production, whatever — drop it in the comments. I collect these.

Top comments (0)