HTTP Status Codes Explained: A Practical Guide
HTTP status codes are three-digit responses that tell a client what happened to its request. A practical tour of the codes that actually matter.
HTTP status codes are three-digit numbers a server sends back with every response to tell the client what happened — whether the request succeeded, needs a redirect, was malformed, or blew up on the server. The first digit tells you the category (2xx success, 4xx client error, 5xx server error); the rest narrows it down. Knowing the common ones — and which ones are commonly misused — saves hours of debugging.
The five categories
Every status code falls into one of five ranges based on its first digit:
- 1xx — Informational. The request was received and processing continues. Rarely seen directly;
101 Switching Protocolsis used when upgrading a connection to WebSocket. - 2xx — Success. The request was received, understood, and accepted.
- 3xx — Redirection. Further action is needed to complete the request, usually following a different URL.
- 4xx — Client error. The request has a problem — bad syntax, missing auth, a resource that doesn’t exist.
- 5xx — Server error. The server failed to fulfill a valid request.
The codes you’ll actually use
200 OK — the default success response. The body contains the requested resource or the result of the operation.
201 Created — a POST successfully created a new resource. Convention is to include a Location header pointing to it.
204 No Content — the request succeeded but there’s nothing to return, common for DELETE and some PUT requests.
301 Moved Permanently / 308 Permanent Redirect — the resource now lives at a new URL, permanently. Search engines transfer ranking signals to the new URL. 301 allows the method to change on redirect (historically ambiguous); 308 preserves it strictly.
302 Found / 307 Temporary Redirect — a temporary redirect. 307 is the stricter, unambiguous version that guarantees the method and body are preserved.
304 Not Modified — sent in response to a conditional request (an If-None-Match or If-Modified-Since header) when the cached copy is still valid. No body is sent, saving bandwidth. This is the backbone of efficient HTTP caching.
400 Bad Request — the server can’t parse the request as sent. Malformed JSON, missing required fields, invalid syntax.
401 Unauthorized — despite the name, this means unauthenticated. The client needs to authenticate — send a bearer token, log in, whatever the API requires. Distinct from 403.
403 Forbidden — the client is authenticated, but not allowed to do this. No amount of re-authenticating fixes it; the permissions are the problem. This distinction matters when designing RBAC vs ABAC systems.
404 Not Found — the resource doesn’t exist at this URL. Also commonly (if imprecisely) used to hide the existence of a resource from unauthorized users, rather than leaking a 403.
405 Method Not Allowed — the URL exists, but doesn’t support this HTTP method. A DELETE to a read-only endpoint, for example.
409 Conflict — the request conflicts with the current state of the resource, such as a version mismatch in optimistic concurrency control.
422 Unprocessable Entity — the request is syntactically valid but semantically wrong — well-formed JSON that fails validation rules. Many APIs use this instead of 400 for validation errors.
429 Too Many Requests — the client has hit a rate limit. Well-behaved APIs include a Retry-After header telling the client how long to back off.
500 Internal Server Error — a generic catch-all for unhandled server-side failures. If you’re returning this to users regularly, something needs better error handling upstream.
502 Bad Gateway — a server acting as a proxy or gateway got an invalid response from an upstream server. Common behind load balancers and reverse proxies.
503 Service Unavailable — the server is temporarily unable to handle the request, often due to overload or maintenance. Should include a Retry-After header when possible.
504 Gateway Timeout — a gateway or proxy didn’t get a timely response from an upstream server.
Status codes commonly misused
A few patterns show up in real APIs that are worth calling out:
- Returning 200 for everything, with an error field in the body. This defeats HTTP semantics — caching, retries, and monitoring tools all key off the status code, not the payload.
- 401 vs 403 confusion. If a client sends no credentials or invalid ones, that’s 401. If the credentials are valid but insufficient, that’s 403.
- 404 instead of 410.
410 Goneexplicitly signals that a resource used to exist and was intentionally removed, which is more useful to caches and crawlers than a plain 404. - 500 for validation errors. A malformed request body is a client problem (4xx), not a server problem (5xx).
Quick reference table
| Range | Meaning | Retry? |
|---|---|---|
| 2xx | Success | N/A |
| 3xx | Redirect — follow the new location | Depends on code |
| 4xx | Client error — fix the request | Usually not, except 429 |
| 5xx | Server error | Often yes, with backoff |
The takeaway
Status codes are a contract, not decoration — clients, caches, load balancers, and monitoring systems all branch on them. Use the specific code that matches what actually happened (401 vs 403, 404 vs 410, 400 vs 422) rather than defaulting to 200 or 500 for everything. Getting this right makes your API self-documenting and lets infrastructure between you and the client — caches, retries, circuit breakers — do its job automatically.
Tagged
Keep reading
Chisato · · 5 min read IDS vs IPS: Intrusion Detection vs Prevention
An IDS watches network traffic and alerts on threats; an IPS sits inline and blocks them automatically. How the two compare and when to use each.
Takina · · 4 min read What Is the Backend-for-Frontend (BFF) Pattern?
A backend-for-frontend (BFF) is a dedicated backend layer for one client type — shaping, aggregating, and simplifying calls to shared downstream APIs.
Takina · · 4 min read HTTP Range Requests and Partial Content, Explained
HTTP range requests let a client ask for just part of a resource, enabling video seeking, resumable downloads, and partial file fetches over HTTP.