How the five HTTP status code classes work
Every HTTP response starts with a three-digit status code defined in RFC 9110. The first digit tells you which of five classes the response belongs to — and once you've internalised the classes, most codes make immediate sense without a lookup.
- 1xx — Informational. The server has received your request and is telling you to hang on. You rarely see these in application code; they matter for WebSocket upgrades (101) and streaming uploads (100).
- 2xx — Success. The request was received, understood, and processed. 200, 201, and 204 are the ones that matter for most APIs.
- 3xx — Redirection. The resource lives somewhere else. The client is expected to make another request to the new location.
- 4xx — Client error. The request is broken in some way that the client should be able to fix. This is where authorization, validation, and missing-resource errors live.
- 5xx — Server error. The server understood the request but failed to complete it. Unlike 4xx, these are usually not the client's fault.
The rule of thumb when debugging: 4xx means "you did something wrong", 5xx means "we did something wrong". If you're on the server side reading logs, 5xx is what pages you at 2 AM — 4xx rarely does.
The status codes you'll actually use
Out of the 60+ codes defined across RFC 9110 and related specs, a short list covers almost every real-world interaction:
200 OK— the request succeeded and the response has a body.201 Created— a POST that successfully created a new resource. Include aLocationheader pointing to it.204 No Content— success, but there's nothing to send back. Typical for DELETE and some PUT responses.301 Moved Permanentlyand308 Permanent Redirect— the resource lives at a new URL forever. 308 preserves the request method (POST stays POST); 301 is historically allowed to switch POST to GET.302 Foundand307 Temporary Redirect— a temporary redirect. 307 preserves method; 302 is historically allowed to switch POST to GET.304 Not Modified— the client's cached copy is still valid. Sent in response to a conditional GET withIf-Modified-SinceorIf-None-Match.400 Bad Request— the request is syntactically broken or has invalid fields. Validation errors typically return 400 with a body describing what failed.401 Unauthorized— the client is not authenticated. Better named "Unauthenticated".403 Forbidden— the client is authenticated but not allowed to access this resource.404 Not Found— the resource doesn't exist, or the server doesn't want to admit it exists.409 Conflict— the request conflicts with the current state. Typical for concurrent edits (optimistic locking failures).422 Unprocessable Content— the request is syntactically correct but semantically invalid. Common in APIs for field-level validation errors.429 Too Many Requests— rate-limited. Should include aRetry-Afterheader.500 Internal Server Error— the catch-all for "something broke and we don't know what".502 Bad Gateway— the server got an invalid response from an upstream service. Very common when a load balancer can't reach a backend.503 Service Unavailable— the server is temporarily overloaded or down for maintenance. Should includeRetry-After.504 Gateway Timeout— the server waited on an upstream service and gave up.
The pairs that trip people up
401 vs 403
The single most mis-used pair. The rule: 401 means "we don't know who you are"; 403 means "we know who you are and the answer is still no". If a logged-out user hits a protected endpoint, return 401. If a logged-in user hits an endpoint they lack permissions for, return 403.
301 vs 308 (and 302 vs 307)
The numbered pair difference is about request method. 301 and 302 are historical — they were defined before HTTP/1.1 clarified behaviour, and many clients rewrite POST to GET on these. 307 and 308 were added specifically to preserve the original method. If you're redirecting a POST and want it to stay a POST, use 307 (temporary) or 308 (permanent). For a GET, any of the four work.
400 vs 422
400 is for requests the server cannot parse at all (malformed JSON, missing required headers, bad URL). 422 is for requests the server parsed successfully but rejected on content grounds (email already taken, date in the past, password too short). Many APIs use 400 for both; strictly, 422 is the right one for validation.
502 vs 503 vs 504
All three are server-side failures but mean different things to oncall. 502 means the upstream returned garbage (process crashed mid-response, protocol mismatch). 503 means the server is up but intentionally refusing traffic (overloaded, maintenance). 504 means the server waited on the upstream past its deadline.
The weird ones
A handful of status codes exist mostly as trivia, but a few are genuinely useful in niche situations:
- 418 I'm a teapot — defined as an April Fool's joke in RFC 2324 (the Hyper Text Coffee Pot Control Protocol). Not part of HTTP proper. Some libraries and sites use it as a novelty "we're not serving you" code, which is exactly what an HTCPCP-compliant teapot would do.
- 451 Unavailable For Legal Reasons — reference to Ray Bradbury's Fahrenheit 451. Used when content is blocked by legal order (court injunction, GDPR takedown, DMCA). Should include a
Linkheader pointing to information about the block. - 425 Too Early — used with TLS 1.3 early data (0-RTT). If the server can't safely process a replay-able request, it returns 425 so the client retries without early data.
- 226 IM Used — for delta encoding, basically unused in modern HTTP.
- 509 Bandwidth Limit Exceeded — not in any RFC, but widely used by shared hosting providers.
Debugging workflow
When an API call returns an unexpected status code, the fastest diagnostic path is:
- Read the first digit. 4xx means the request is broken; 5xx means the server is broken. This tells you whose logs to look at first.
- Check the response body. Most modern APIs return a JSON error object with a
code,message, and sometimes atrace_id. The body is usually more informative than the status code alone. - Check specific headers.
Retry-Afteron 429/503 tells you when to try again.WWW-Authenticateon 401 tells you which auth scheme the server expects.Locationon 3xx tells you where to go next. - Reproduce with
curl -i. The-iflag prints response headers, which often contain the clue you need.-vprints the full request too.
Related HTTP & network tools
- URL Parser — decode a URL into scheme, host, path, query, and fragment.
- URL Encode / Decode — percent-encoding for query strings and path segments.
- MIME Type Lookup — for the
Content-Typeheader values you can never remember. - JWT Decoder — inspect the bearer token from your
Authorizationheader. - Base64 — decode basic-auth headers and data-URL payloads.