DevToolbox
All articles

HTTP Status Codes: The Complete Developer Reference

· 10 min read

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
Try it free
HTTP Status Codes Reference
100% client-side · no signup · no upload
Open tool →