← Learn··Updated 5 Jul 2026·3 min read

HTTP status codes explained

A short reference on HTTP status codes — the three-digit grammar, what each class means, the twenty codes you actually meet, 401 vs 403, 301 vs 302, the 502/503/504 triage table for reverse proxies, and the retry semantics most clients get wrong.

Programming
Web & HTTP
#web
#http
#protocols
#debugging

The one-line definition

An HTTP status code is the three-digit number a server returns with every response, and it is a sentence in a tiny grammar: the first digit names who succeeded or failed, the last two name how. Learn the five classes and you can triage most web failures before reading a single log line. The current definitions live in RFC 9110; the grammar itself is older than the web, inherited from FTP-era protocols.

The five classes

Class Meaning Mnemonic
1xx Informational — "keep going" hold on
2xx Success here you go
3xx Redirection — look elsewhere go away (politely)
4xx Client error — your request is wrong you messed up
5xx Server error — my handling broke I messed up

The 4/5 split is the load-bearing distinction: it assigns blame. A 4xx says fix the request; a 5xx says fix the server. Every debugging session starts by believing the first digit.

The codes you actually meet

Code Name When you see it
200 OK The happy path
201 Created POST succeeded, new resource exists
204 No Content Success with an empty body (DELETEs, OPTIONS)
301 Moved Permanently Redirect that browsers and search engines cache
302 Found Temporary redirect; nothing should be cached
304 Not Modified Your cached copy is still good — the caching win
400 Bad Request Malformed body, invalid JSON, missing field
401 Unauthorized Misnamed: means unauthenticated — who are you?
403 Forbidden Authenticated, but not allowed — I know you, no
404 Not Found The famous one — nothing at this path
405 Method Not Allowed Right path, wrong verb (POST to a GET route)
409 Conflict State collision — duplicate create, stale update
410 Gone Like 404, but "and it's never coming back" — deliberate
418 I'm a teapot The 1998 April-1 coffee-pot joke that shipped; a favorite for blocking bots
422 Unprocessable Entity Parsed fine, semantically invalid — the validation-error workhorse
429 Too Many Requests Rate limited; honor Retry-After
500 Internal Server Error Unhandled exception, the generic server crash
502 Bad Gateway Proxy reached upstream, got garbage or a refusal
503 Service Unavailable Server up but not serving — overloaded, draining, booting
504 Gateway Timeout Proxy reached upstream, got silence until the deadline

The pairs people mix up

401 vs 403 — 401 means authenticate (you haven't proven who you are; send credentials); 403 means authorization failed (identity fine, permission denied). If retrying with a login could help: 401. If no login will ever help: 403.

301 vs 302 — permanence is a promise to caches. A 301 gets remembered by browsers aggressively and transfers SEO authority; a mistaken 301 haunts users until their cache dies. When unsure, 302 is the reversible mistake.

404 vs 410 — 404 is "nothing here (right now, who knows)"; 410 is an affidavit that the thing existed and was removed. Crawlers deindex 410s faster.

The reverse-proxy triage table

Behind a reverse proxy like Caddy, the 50x triplet tells you exactly where to look:

  • 502 — proxy reached the app and got a broken answer: app crashed mid-response, wrong port, TLS1 mismatch. Look at the app's logs.
  • 503 — someone is deliberately not serving: app still starting, health check failing, maintenance mode. Look at readiness.
  • 504 — the app never answered: hung request, dead upstream host, firewall eating packets. Look at timeouts and connectivity.
🔗 Learn more1 What is TLS (and how does Let's Encrypt fit)?

Memorize the triplet and "the site is down" becomes a routing decision.

The footguns

200 with an error body. APIs that return 200 {"error": ...} defeat every piece of generic tooling — monitors, retriers, caches all believe the first digit. The status line is the contract; use it.

Retry semantics. 429 and 503 are the invitations to retry (with backoff, honoring Retry-After). Blind retries on 4xx are a bug: the request was wrong once, it will be wrong again — and retried non-idempotent2 POSTs are how double charges happen.

🔗 Learn more2 What is idempotency (in data pipelines)?

Custom codes. Inventing 599 or 250 technically works and breaks intermediaries subtly. The registry exists; 422/409/503 cover nearly every "custom" need.