What status codes are and where they come from
Every HTTP response begins with a status line that includes a numeric status code and a textual reason phrase. The code is a three-digit number from 100 to 599, and the reason phrase is a short human-readable string like OK or Not Found. The codes are defined in a series of RFCs, starting with RFC 1945 (HTTP/1.0) and extended by RFC 2616 (HTTP/1.1) and RFC 9110 (the current HTTP semantics standard, published in 2022). The Internet Assigned Numbers Authority (IANA) maintains the official registry of status codes, which is the source of truth for what each code means.
The codes are grouped into five classes by their first digit. Codes in the 100s are informational, the 200s indicate success, the 300s indicate redirection, the 400s indicate client errors, and the 500s indicate server errors. Within each class, the second and third digits narrow the meaning. The grouping is what makes the codes easy to learn: once you know the class, you know roughly what happened, and the specific code adds detail.
The reason phrase is mostly historical. HTTP/2, published in 2015, dropped the reason phrase from the wire format because it added bytes without adding information that the code did not already carry. Browsers and clients use the code, not the phrase, to decide what to do. Custom reason phrases are still allowed in HTTP/1.1 but have no effect on behavior. The phrase you see in your browser's network tab is generated by the client from the code, not transmitted by the server.
1xx and 2xx — information and success
The 1xx class is informational: the server has received the request and is continuing to process it. The most common is 100 Continue, sent in response to a request with an Expect: 100-continue header, which lets the client check whether the server is willing to accept a large request body before sending it. 101 Switching Protocols is used during the WebSocket handshake to confirm the upgrade from HTTP to the WebSocket protocol. 103 Early Hints, standardized in 2022, lets the server send preload hints before the final response is ready.
The 2xx class indicates success. 200 OK is the standard success code for GET, POST, and PUT requests; the response body contains the resource or the result. 201 Created is returned by POST requests that create a new resource, with the new resource's URL in the Location header. 202 Accepted means the request has been accepted for processing but the processing is not yet complete, which is common for long-running operations like video transcoding or report generation. 204 No Content indicates success without a response body, often used for DELETE and PUT requests where the client already has the latest state.
206 Partial Content is the status code for range requests, where the client asks for only part of a resource using the Range header. This is what makes video streaming and resumable downloads work: the client requests bytes 0 to 1,000,000, the server responds with 206 and those bytes, and the client requests the next range. The Content-Range header in the response specifies which bytes were sent. Without 206, large file downloads would have to start over from the beginning every time the connection dropped.
3xx — redirection and caching
The 3xx class indicates that the client needs to take additional action to complete the request, usually by following a redirect to a different URL. 301 Moved Permanently and 308 Permanent Redirect tell the client that the resource has a new permanent URL and that future requests should use the new URL. Search engines update their indexes to the new URL when they see a 301, which is why it is the standard code for site migrations. The difference between 301 and 308 is that 308 preserves the request method (a POST stays a POST), while 301 historically allowed clients to convert a POST to a GET, which was a frequent source of bugs.
302 Found and 307 Temporary Redirect are the temporary equivalents. The resource is at a different URL for now, but future requests should still try the original URL. 302 has the same method-conversion ambiguity as 301; 307 preserves the method. 303 See Other is the explicit redirect-with-a-GET code, used after a POST to redirect the client to a result page without re-submitting the POST if the user refreshes. The Post/Redirect/Get pattern, which prevents duplicate form submissions, uses 303.
304 Not Modified is the most important 3xx code for performance. When a client has a cached copy of a resource and sends a conditional request with the If-Modified-Since or If-None-Match header, the server can respond with 304 and no body, telling the client to use its cached copy. This saves bandwidth and server load. A well-configured server returns 304 for static assets more often than 200, because most requests for static assets come from clients that already have a valid cached copy.
4xx — client errors
The 4xx class indicates that the client made a mistake. 400 Bad Request is the generic client error: the request was malformed, missing required parameters, or otherwise invalid. Many APIs use 400 for all validation errors, which is acceptable but less specific than it could be. RFC 9457 (Problem Details for HTTP APIs) defines a standard JSON format for error responses that includes a type, title, status, detail, and instance, which is worth adopting for any API that returns structured errors.
401 Unauthorized actually means unauthenticated — the request lacks valid authentication credentials for the target resource. The response must include a WWW-Authenticate header describing how to authenticate. 403 Forbidden means the server understood the request and the client is authenticated, but the server is refusing to fulfill it. The difference is subtle but important: 401 means who are you, 403 means I know who you are and you cannot do this. Returning 401 when you mean 403 leaks information about whether a user account exists.
404 Not Found is the most familiar client error: the requested resource does not exist. 405 Method Not Allowed means the resource exists but does not support the requested HTTP method (e.g., a PUT to a read-only resource); the response must include an Allow header listing the supported methods. 406 Not Acceptable means the server cannot produce a response matching the Accept header's content negotiation. 409 Conflict indicates that the request conflicts with the current state of the resource, such as trying to create a user with an email that already exists.
418 I'm a Teapot is an April Fools' joke from RFC 2324 (the Hyper Text Coffee Pot Control Protocol, published in 1998). It is registered in the IANA status code registry, which means it cannot be reused for anything else. Some servers return it as an Easter egg for requests they consider nonsensical. 422 Unprocessable Entity, from the WebDAV standard, is used when the request is well-formed and authenticated but the server cannot process it due to semantic errors (e.g., a JSON body that is valid JSON but fails business validation). 429 Too Many Requests is the standard rate-limiting response, with an optional Retry-After header telling the client when to try again.
5xx — server errors
The 5xx class indicates that the server failed to fulfill a valid request. 500 Internal Server Error is the generic server error: something went wrong on the server side and the request could not be completed. Most unhandled exceptions in a web application produce a 500. The response body should not include a stack trace in production, because that leaks information about the application's internals to attackers; log the full error server-side and return a generic message to the client.
501 Not Implemented means the server does not support the functionality required to fulfill the request, usually because the HTTP method is unrecognized. 502 Bad Gateway means the server, while acting as a gateway or proxy, received an invalid response from the upstream server. This is common when a load balancer cannot reach a backend, or when the backend crashes mid-request. 503 Service Unavailable means the server is temporarily unable to handle the request, usually due to maintenance or overload; the response should include a Retry-After header if possible. 504 Gateway Timeout means the server, acting as a gateway, did not receive a timely response from the upstream server.
503 is the correct code for planned maintenance and for overload shedding. Returning 503 with a Retry-After tells clients (including search engine crawlers) to come back later, rather than treating the outage as permanent. Returning 500 during maintenance is wrong because clients may interpret it as a bug and not retry. The difference matters for SEO: Google treats 503 as temporary and will retry, while 500 may cause deindexing if it persists.
Status codes in practice: APIs, browsers, and debugging
When designing an API, the most important rule is consistency. Pick a small set of codes and use them consistently across all endpoints. A common, defensible set is: 200 for successful retrieval, 201 for successful creation, 204 for successful deletion, 400 for validation errors, 401 for missing or invalid authentication, 403 for insufficient permissions, 404 for missing resources, 409 for conflicts, 422 for semantic errors, 429 for rate limiting, and 500 for unexpected server errors. Avoid the exotic codes unless you have a specific reason; they confuse clients and add no value.
When debugging, the status code is the first thing to look at. A 4xx error means the request is wrong; fix the client. A 5xx error means the server is wrong; fix the server. A 3xx with an unexpected Location header means a redirect is misconfigured. A 2xx with the wrong body means the routing is wrong or the response serialization has a bug. The status code narrows the problem to a specific layer, which is much faster than reading the response body first.
Browser behavior depends on the status code in ways that matter. 301 redirects are cached aggressively, sometimes permanently, which can break a site if a 301 is sent by mistake; the fix is usually to clear the browser cache or to use 302 for testing. 304 responses are served from cache without revalidation, which is good for performance but means changes to a static asset may not appear until the cache is invalidated; appending a version hash to the asset URL (cache busting) is the standard workaround. 401 and 407 trigger the browser's authentication dialog, which is rarely what modern web applications want; use a custom login flow instead and return 401 only for API endpoints.