HTTP Status Codes: The Complete Developer Reference
Every HTTP request your browser or API client sends receives a three-digit numeric response code from the server. These codes are not arbitrary — they form a structured vocabulary that tells the client exactly what happened and, in many cases, what to do next. Understanding them deeply is essential for building reliable REST APIs, debugging production issues quickly, and writing clients that handle failure gracefully.
This reference covers every status code you are likely to encounter in real-world web development, with explanations, concrete API examples, and the most common mistakes developers make when choosing which code to return.
How to Read a Status Code
HTTP status codes are three-digit integers. The first digit defines the class of the response. The remaining two digits provide more detail within that class. There are five classes:
- 1xx — Informational: The request was received; the process is continuing.
- 2xx — Success: The request was received, understood, and accepted.
- 3xx — Redirection: Further action is needed to complete the request.
- 4xx — Client Error: The request contains bad syntax or cannot be fulfilled.
- 5xx — Server Error: The server failed to fulfil an apparently valid request.
A well-designed HTTP client only needs to understand the class (the first digit) to decide the broad strokes of what to do. The specific code within the class tells it exactly what happened. This two-layer design is intentional: it allows new specific codes to be added over time without breaking clients that only handle the class.
1xx Informational
Informational responses are provisional. They indicate that the server has received the request headers and that the client should proceed. In practice, most developers rarely deal with 1xx codes directly — they are handled transparently by HTTP libraries and proxies.
100 Continue
The server has received the request headers and the client should proceed to send the request body. This is used when a client wants to check whether the server will accept a large request before sending it. For example, a client uploading a 50 MB file can send the headers first with an Expect: 100-continue header. If the server responds with 100, the client sends the body. If the server responds with 413 (Payload Too Large), the client avoids wasting bandwidth.
101 Switching Protocols
The server agrees to switch protocols as requested by the client. This is used when upgrading from HTTP/1.1 to WebSocket. The response includes an Upgrade header specifying the new protocol. After this response, the connection is no longer HTTP — it becomes a WebSocket connection.
2xx Success
The 2xx class indicates that the client's request was received, understood, and accepted. Choosing the right 2xx code makes your API self-documenting and easier to consume.
200 OK
The standard success response. The meaning of the response body depends on the request method. For GET, the body contains the requested resource. For POST, it contains the result of the action. For PUT or PATCH, it contains the updated resource.
GET /users/42
→ 200 OK
{
"id": 42,
"name": "Alice",
"email": "[email protected]"
} 201 Created
The request succeeded and a new resource was created as a result. This is the correct response for a successful POST that creates a resource. The response should include a Location header pointing to the newly created resource.
POST /users
{ "name": "Bob", "email": "[email protected]" }
→ 201 Created
Location: /users/43
{
"id": 43,
"name": "Bob",
"email": "[email protected]"
} 202 Accepted
The request has been accepted for processing but the processing has not been completed. This is used for asynchronous operations. For example, triggering a report generation job returns 202 immediately. The client can poll a separate status endpoint to check progress. Returning 200 in this situation would be misleading because the work is not done yet.
POST /reports/generate
{ "type": "monthly", "month": "2026-05" }
→ 202 Accepted
{
"jobId": "report-789",
"statusUrl": "/jobs/report-789"
} 204 No Content
The request succeeded but there is no content to return. This is the correct response for DELETE requests and for PUT / PATCH requests when you choose not to return the updated resource. The response must not include a body. Returning 200 with an empty body instead of 204 is technically incorrect.
DELETE /users/42
→ 204 No Content 206 Partial Content
The server is delivering only part of the resource due to a range request from the client. This powers resumable downloads and video streaming. The client sends a Range header specifying the byte range it wants; the server responds with 206 and a Content-Range header.
GET /videos/intro.mp4
Range: bytes=0-1048575
→ 206 Partial Content
Content-Range: bytes 0-1048575/52428800
Content-Length: 1048576 3xx Redirection
Redirection responses tell the client that it needs to make a further request to a different URL. The specific code controls whether the redirect is permanent or temporary and whether the original HTTP method should be preserved on the follow-up request.
301 Moved Permanently
The resource has been moved to a new URL permanently. Browsers and search engines should update their bookmarks and indexes. Historically, many browsers changed POST to GET when following a 301 redirect — a behaviour that was technically incorrect but widespread. If you need the method preserved on a permanent redirect, use 308 instead.
302 Found
The resource is temporarily at a different URL. The original URL should be used for future requests. Like 301, many browsers historically changed POST to GET when following a 302. If you need to temporarily redirect and preserve the method, use 307 instead.
304 Not Modified
The response has not been modified since the client's last request. This is the foundation of HTTP caching. The client sends an If-None-Match header (with an ETag value) or an If-Modified-Since header. If the content has not changed, the server returns 304 with no body, and the client uses its cached copy. This saves bandwidth on both sides.
307 Temporary Redirect
The resource is temporarily at a different URL and the client must use the same method and body when following the redirect. This is the method-preserving version of 302. If you temporarily redirect a POST /checkout to another URL, use 307 to ensure the client does not silently switch to a GET.
308 Permanent Redirect
The resource has permanently moved to a new URL and the client must use the same method and body when following the redirect. This is the method-preserving version of 301. Use 308 instead of 301 when permanently redirecting POST or PUT endpoints.
To summarise the redirect differences:
Permanent, method may change: 301 Moved Permanently
Temporary, method may change: 302 Found
Temporary, method preserved: 307 Temporary Redirect
Permanent, method preserved: 308 Permanent Redirect 4xx Client Errors
The 4xx class indicates that the error originated on the client side. The request contained bad syntax, referenced a resource that does not exist, or the client does not have permission to perform the action. These errors are the client's responsibility to fix.
400 Bad Request
The server cannot process the request because of a client-side error — malformed JSON, invalid query parameters, a missing required field, or a constraint violation that the server caught early. Always include a descriptive error body explaining what was wrong so the developer calling your API can fix it.
POST /users
{ "email": "not-an-email" }
→ 400 Bad Request
{
"error": "validation_failed",
"message": "The 'email' field must be a valid email address."
} 401 Unauthorized
The client must authenticate itself to get the requested response. The name is slightly misleading — 401 is about authentication, not authorization. The client has not provided valid credentials (or has not provided any at all). The response should include a WWW-Authenticate header telling the client how to authenticate.
403 Forbidden
The client is authenticated but does not have permission to access the resource. This is about authorization. The server knows who you are (you are logged in), but you are not allowed to do what you are asking. For example, a regular user trying to access an admin endpoint receives 403, not 401.
The distinction is critical and commonly confused:
401 Unauthorized → "Who are you? Please log in first."
403 Forbidden → "I know who you are, but you can't do this." 404 Not Found
The server cannot find the requested resource. This is the most famous HTTP status code. It is used when a URL does not correspond to any resource. Note that a server may also return 404 instead of 403 to avoid revealing that a resource exists but is forbidden — a deliberate security measure.
405 Method Not Allowed
The request method is not supported for the target resource. For example, sending a DELETE request to an endpoint that only supports GET and POST. The response must include an Allow header listing the supported methods.
DELETE /config/global
→ 405 Method Not Allowed
Allow: GET, POST 409 Conflict
The request conflicts with the current state of the server. Common examples: trying to create a user with an email address that is already registered, or trying to update a record that has been modified by another client since you last fetched it (optimistic locking conflict).
410 Gone
The resource previously existed but has been permanently deleted and will not be available again. Unlike 404, which is ambiguous about whether the resource ever existed, 410 tells the client definitively that the resource is gone for good. Search engines should deindex 410 URLs more aggressively than 404 URLs.
422 Unprocessable Entity
The server understands the content type and the syntax is correct, but it cannot process the contained instructions. This is subtly different from 400. With 400, the request is syntactically malformed. With 422, the syntax is fine but the semantics are invalid. For example, a well-formed JSON body that fails business rule validation (a start date that is after the end date) returns 422.
429 Too Many Requests
The client has sent too many requests in a given amount of time — rate limiting. The response should include a Retry-After header indicating how long to wait before making a new request, and optionally X-RateLimit-Limit and X-RateLimit-Remaining headers.
GET /api/data
→ 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0 5xx Server Errors
The 5xx class indicates that the server is aware that it has encountered an error or is otherwise incapable of performing the request. These are the server's problem to fix, not the client's.
500 Internal Server Error
A generic catch-all for unexpected server-side failures. This is what you return when something goes wrong that you did not anticipate — an unhandled exception, a null pointer dereference, an unexpected database state. Every 500 should be logged and investigated. The response body should not expose internal stack traces or error details to the client in production.
Debugging tips: Check your application logs immediately. Look for unhandled exceptions or panics. Check memory and CPU usage — the process may be resource-starved. If the error is intermittent, look for race conditions or deadlocks.
502 Bad Gateway
The server, acting as a gateway or proxy, received an invalid response from an upstream server. You will typically see this when Nginx or a load balancer cannot reach your application server, or when your application server crashes mid-request. The gateway received something it could not parse as a valid HTTP response.
Debugging tips: Check that the upstream application server is running. Verify firewall rules and network connectivity between the gateway and the upstream. Check the upstream server's logs for crashes or errors.
503 Service Unavailable
The server is not ready to handle the request. Common causes include the server being overloaded or down for maintenance. Unlike 500, which is unexpected, 503 is often intentional — it is returned during planned maintenance windows or when a circuit breaker trips to protect a degraded service. Include a Retry-After header when the downtime is expected to be brief.
Debugging tips: Check the server's load (CPU, memory, connection pool exhaustion). Look for upstream dependencies that may be failing. Check if a deployment or maintenance process is running.
504 Gateway Timeout
The server, acting as a gateway or proxy, did not receive a timely response from an upstream server. Similar to 502, but specifically about a timeout rather than an invalid response. The upstream server may be alive but taking too long to respond — possibly due to a slow database query, an external API being slow, or a deadlock.
Debugging tips: Check upstream response times. Look for slow database queries. Check external API latency. Consider whether your gateway timeout configuration needs to be adjusted or whether the upstream work needs to be made asynchronous.
Common Mistakes
Returning 200 for errors
The most widespread antipattern in API design is returning 200 OK with an error in the body. This forces clients to parse the body of every response to determine whether it succeeded, breaking the fundamental HTTP contract. It also prevents HTTP-level monitoring tools, load balancers, and caches from working correctly.
Bad:
→ 200 OK
{ "success": false, "error": "User not found" }
Good:
→ 404 Not Found
{ "error": "user_not_found", "message": "No user with ID 42 exists." } Using 403 when you mean 401
If a user is not logged in and tries to access a protected resource, return 401 — not 403. Reserve 403 for cases where the user is authenticated but lacks the required permission. Returning 403 to unauthenticated users confuses clients and makes it harder to build proper authentication flows.
Using 500 for validation errors
If a client sends invalid input and your server throws an uncaught exception as a result, the fix is to validate the input first and return 400 or 422 — not to let the exception propagate into a 500. A 500 should represent a genuine unexpected failure, not a predictable invalid-input scenario.
Not including helpful error bodies
An error code alone is not enough for an API consumer. Always include a machine-readable error code (a string constant, not just the HTTP status number) and a human-readable message. The machine-readable code lets clients handle specific error cases programmatically; the human-readable message helps developers debug during integration.
Quick Reference
Code Meaning
---- -------
100 Continue
101 Switching Protocols
200 OK
201 Created
202 Accepted
204 No Content
206 Partial Content
301 Moved Permanently
302 Found (temporary redirect)
304 Not Modified
307 Temporary Redirect (method preserved)
308 Permanent Redirect (method preserved)
400 Bad Request
401 Unauthorized (authentication required)
403 Forbidden (authenticated but not permitted)
404 Not Found
405 Method Not Allowed
409 Conflict
410 Gone
422 Unprocessable Entity
429 Too Many Requests
500 Internal Server Error
502 Bad Gateway
503 Service Unavailable
504 Gateway Timeout