HTTP status codes are three-digit numbers a server returns to describe the outcome of a request. They fall into five classes by first digit: 1xx informational, 2xx success, 3xx redirection, 4xx client error, and 5xx server error. The first digit alone tells you the result type.
If you build for the web, you read status codes every day, and you also emit them every day. Yet the difference between a well-chosen code and a lazy one is not cosmetic. Browsers cache some codes and not others. Content delivery networks retry certain 5xx responses automatically. Search engines pass ranking signals through a 301 but not a 302. Client libraries branch their entire error handling on the class of the response. Pick the wrong number and machines downstream do the wrong thing, silently. This guide walks through the five classes, the individual codes worth memorizing, and the pairs that trip up even experienced developers.
What do the five status-code classes mean?
The status code system is defined by RFC 9110 (HTTP Semantics, published in 2022, which replaced the older RFC 7231). The genius of the design is that the first digit is a category you can act on without reading the rest. A client can decide how to react by inspecting one digit.
- 1xx Informational — Interim responses. The server received the request and is still working. These are provisional and are usually invisible to application code, but they matter for protocol negotiation and performance (for example
100 Continueand103 Early Hints). - 2xx Success — The request was received, understood, and accepted. The action succeeded.
- 3xx Redirection — Further action is needed to complete the request, usually following the request to a different URL. This class also covers caching validation.
- 4xx Client Error — The request was faulty: bad syntax, missing authentication, a resource that does not exist, or a rule the client broke. Repeating the identical request will usually fail the same way.
- 5xx Server Error — The request may have been valid, but the server failed to fulfill it. The problem is on the server side, and the client is often invited to retry.
The single most useful habit is honesty about which side owns the failure. A 4xx says “you sent something wrong.” A 5xx says “I broke.” Returning a 5xx for what is really a client mistake sends engineers chasing phantom outages; returning a 200 with an error buried in the body defeats every tool that reads the status line.
Which status codes matter most?
You do not need to memorize the full registry. A working developer can get almost everything right with a couple dozen codes. The table below is a practical reference for the ones that come up daily.
| Code | Name | When to use it |
|---|---|---|
200 |
OK | Standard success with a response body (GET returning data, or an update that echoes the result). |
201 |
Created | A request created a new resource. Include a Location header pointing to it. |
204 |
No Content | Success with deliberately no body. Common for DELETE and for PUT/PATCH that return nothing. |
301 |
Moved Permanently | A URL has moved for good. Cached aggressively; passes SEO signal to the new URL. |
302 |
Found | Temporary redirect. The original URL still applies for future requests. |
304 |
Not Modified | Cache validation: the client’s cached copy is still fresh, so no body is sent. |
400 |
Bad Request | The request is malformed or unparseable (bad JSON, missing required fields). |
401 |
Unauthorized | Authentication is missing or invalid. Send a WWW-Authenticate header. |
403 |
Forbidden | Authenticated but not permitted. Re-authenticating will not help. |
404 |
Not Found | The resource does not exist (or you are hiding its existence deliberately). |
429 |
Too Many Requests | Rate limit exceeded. Pair with a Retry-After header. |
500 |
Internal Server Error | An unexpected, unhandled failure on the server. The catch-all 5xx. |
502 |
Bad Gateway | A proxy got an invalid response from an upstream server. |
503 |
Service Unavailable | Temporarily down: overloaded or in maintenance. Send Retry-After. |
504 |
Gateway Timeout | A proxy waited for an upstream server and it never answered in time. |
Success codes worth distinguishing
Most APIs lean on 200 for everything, but the extra 2xx codes carry information. 201 Created tells the client a new resource now exists and, with a Location header, where to find it. 202 Accepted signals that a request was queued for asynchronous processing and has not finished yet, which is honest for background jobs. 204 No Content is the right answer when there is genuinely nothing to return, and it saves the client from parsing an empty body it did not expect.
Redirection and caching
The 3xx class does two jobs. Most of it is redirection, telling the client to look elsewhere. But 304 Not Modified is a caching response: when a client sends a conditional request with an If-None-Match (ETag) or If-Modified-Since header, a 304 tells it the cached copy is still valid, so the server skips resending the body. That single code saves an enormous amount of bandwidth across the web.
What are the most confused status codes?
A handful of pairs cause the majority of real-world mistakes. Getting these right separates careful APIs from sloppy ones.
401 vs 403
This is the classic. 401 Unauthorized is misnamed: it really means unauthenticated. You sent no credentials, or the ones you sent were invalid or expired. A correct 401 includes a WWW-Authenticate header telling the client how to authenticate. 403 Forbidden means the server understood exactly who you are and still refuses. Re-authenticating fixes a 401; it does nothing for a 403. A frequent bug is returning 403 to anonymous callers who should get a 401, or using 401 when the user is logged in but simply lacks permission.
301 vs 302 (and 307/308)
301 Moved Permanently is cached aggressively by browsers and consolidated by search engines, which transfer ranking signals to the destination and may drop the old URL from their index. 302 Found is temporary: crawlers keep the original URL indexed and clients keep requesting it. Choosing 302 for a genuinely permanent move wastes SEO equity; choosing 301 for a temporary one can strand users on a URL you cached into their browser for months. There is a further wrinkle: historically, clients would change a POST to a GET when following 301 and 302. If you must preserve the request method (for a redirected POST or PUT), use 307 Temporary Redirect or 308 Permanent Redirect, which guarantee the method and body are kept.
200 vs 204
Both mean success. 200 OK promises a body; the client will try to read and parse one. 204 No Content promises the opposite: the operation worked and there is intentionally nothing to send, so the client should keep its current state or view. Returning 200 with an empty body forces clients to handle an awkward edge case; returning 204 states your intent clearly. It is the natural response to a successful DELETE.
400 vs 422
400 Bad Request covers requests the server cannot even parse: malformed JSON, a broken query string, missing required structure. 422 Unprocessable Content is for requests that are syntactically valid but fail business or validation rules, such as an email that is well-formed but already taken. Not everyone draws this line, and 400 remains a defensible catch-all, but the distinction helps clients tell “I sent garbage” from “you rejected my valid-looking data.”
502 vs 503 vs 504
These three all report server-side trouble but for different reasons. 502 Bad Gateway means a proxy or load balancer received an invalid or broken response from the upstream service it forwards to, often because that service crashed. 503 Service Unavailable means the server itself is temporarily unable to handle the request, typically overload or planned maintenance, and it should include a Retry-After header so clients and CDNs know when to try again. 504 Gateway Timeout means a proxy waited for an upstream server and got nothing back in time. In short: 502 is a bad answer, 504 is no answer, and 503 is “not now, come back later.”
How should APIs choose the right code?
Status codes are a contract with every machine between your server and the client, so treat them as infrastructure, not decoration. A few principles keep you out of trouble.
- Let the class match the culprit. Never return a 5xx for a client mistake, and never bury an error inside a 200. Monitoring, retries, and alerting all read the status line first.
- Use the specific code, not just the class default.
201and204tell clients more than a bare200;429and503invite a retry that400does not. - Send the headers that codes imply. A 401 without
WWW-Authenticate, a 201 withoutLocation, or a 429/503 withoutRetry-Afteris a half-finished response. - Be deliberate about retries. Clients and CDNs will retry many 5xx and 429 responses automatically. Make sure the codes you emit match whether retrying is actually safe.
- Do not leak information. Sometimes a 404 is the right answer for a resource the caller is not allowed to know exists, in preference to a 403 that confirms it does.
The reward for getting this right is that everything downstream behaves. Caches cache correctly, retries fire only when they help, error dashboards stay accurate, and other developers reading your API can predict its behavior from the status line alone. That predictability is the whole point of a shared standard, and it costs nothing but a moment of care when you write the response.