Is that a 401 or a 403? A 404 or a 410? HTTP status codes have precise meanings that most developers only half-remember. Getting them right matters — clients use these codes to decide how to handle responses, caches use them to decide what to store, and monitoring systems use them to detect problems.
Here's the reference you can actually use.
The Five Classes
Status codes are grouped into five ranges by their first digit:
- 1xx — Informational (rarely seen in application code)
- 2xx — Success
- 3xx — Redirection
- 4xx — Client error (the request was wrong)
- 5xx — Server error (the request was fine, but something broke server-side)
The most important distinction is 4xx vs 5xx. A 4xx means the client should fix something before retrying. A 5xx means the server broke, and the client might succeed if it tries again later.
1xx: Informational
You'll rarely deal with these in application code, but two are worth knowing.
100 Continue — The server is telling the client it can proceed to send the request body. Used when the client sends an Expect: 100-continue header to check if the server will accept a large upload before actually sending it.
101 Switching Protocols — The connection is being upgraded, most commonly from HTTP to WebSocket. You'll see this in WebSocket handshakes.
2xx: Success
200 OK
The workhorse. Request succeeded, and the response body contains the result. Used for successful GET, PUT, and PATCH responses. Most APIs overuse this by returning 200 for everything — including errors — which forces clients to parse every response body before knowing if the call worked.
201 Created
A resource was created, typically as the result of a POST request. Always include a Location header pointing to the new resource's URL:
HTTP/1.1 201 Created
Location: /api/users/1042
Content-Type: application/json
{ "id": 1042, "name": "Alice" }
202 Accepted
The request was received and will be processed asynchronously. The response typically includes a URL to poll for status. Used for long-running jobs — video encoding, bulk data imports, report generation.
204 No Content
Request succeeded but there's nothing to return. The canonical response for DELETE operations and for PATCH/PUT when you don't want to return the updated resource. No response body — returning one with a 204 violates the spec.
3xx: Redirection
301 Moved Permanently
The resource has permanently moved to a new URL (in the Location header). Clients and search engines should update their references. The browser caches this and won't hit the old URL again without clearing cache.
302 Found (Temporary Redirect)
The resource is temporarily at a different URL. Clients should keep using the original URL for future requests. Overused in practice — most "temporary" redirects are actually permanent.
303 See Other
After a POST/PUT/DELETE, redirect the client to a GET URL. This is the correct redirect to use in the Post-Redirect-Get pattern — it prevents the "do you want to resubmit?" browser prompt on back navigation.
304 Not Modified
The client sent a conditional GET with If-Modified-Since or If-None-Match, and the resource hasn't changed. No body is returned — the client should use its cached copy. This is how HTTP caching works efficiently.
307 Temporary Redirect / 308 Permanent Redirect
Like 302 and 301, but these explicitly preserve the HTTP method. A 302 redirect after a POST technically should become a GET for the new URL (though browsers mostly don't enforce this). 307 guarantees the POST stays a POST. Use 307/308 when method preservation matters — for example, when proxying API requests.
4xx: Client Errors
This is where the nuance lives.
400 Bad Request
The server couldn't understand the request due to malformed syntax, invalid parameters, or missing required fields. The catch-all for client-side input problems when no more specific code applies.
401 Unauthorized
Confusingly named — this actually means unauthenticated. The request lacks valid authentication credentials. The response should include a WWW-Authenticate header describing how to authenticate. The client should retry after providing credentials.
403 Forbidden
The client is authenticated but not authorized to access the resource. The server understood the request and knows who you are — it just won't let you do that. Retrying with the same credentials won't help.
The 401 vs 403 confusion is extremely common. If the user isn't logged in: 401. If the user is logged in but doesn't have permission: 403.
404 Not Found
The resource doesn't exist at this URL. This could be temporary (a resource that hasn't been created yet) or permanent. It tells the client nothing about whether the resource ever existed or might exist in the future.
410 Gone
The resource existed but has been permanently deleted. Unlike 404, this explicitly tells clients (and search engine crawlers) to remove their cached references. Use 410 when you remove a page or resource intentionally and want crawlers to deindex it.
405 Method Not Allowed
The HTTP method is not supported for this endpoint. A GET-only endpoint receiving a DELETE should return 405. Always include an Allow header listing the supported methods:
HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, OPTIONS
409 Conflict
The request conflicts with the current state of the resource. Classic examples: trying to create a resource that already exists (duplicate unique key), or a version conflict in optimistic locking.
422 Unprocessable Entity
The request parsed fine but is semantically wrong. Use this for business logic validation failures — a valid JSON body where a date range has end before start, or an age field contains a negative number, or a discount percentage exceeds 100. These are structurally correct values that fail application-level rules. Some APIs use 400 for everything; 422 is more precise and more useful to clients. (A duplicate unique value like a taken username is better served by 409 Conflict, not 422.)
429 Too Many Requests
The client has exceeded the rate limit. Always include Retry-After (in seconds) and ideally X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers so clients can back off intelligently:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1736001000
5xx: Server Errors
500 Internal Server Error
Something went wrong on the server that wasn't the client's fault. The generic catch-all for unhandled exceptions. The client can retry, potentially with exponential backoff.
Never expose stack traces or internal error details in a 500 response to external clients. Log the details server-side and return a sanitized message.
502 Bad Gateway
The server was acting as a gateway (reverse proxy) and received an invalid response from the upstream server. If Caddy or Nginx returns this, your application server is likely down.
503 Service Unavailable
The server is temporarily unable to handle requests — overloaded or in maintenance. Include a Retry-After header if you know when it'll be back.
504 Gateway Timeout
Like 502, but the upstream server took too long to respond rather than responding incorrectly. Common when a backend service is slow or the network path has an issue.
Quick Reference: The Most Confused Pairs
| Pair | Rule |
|---|---|
| 401 vs 403 | 401 = not authenticated; 403 = authenticated but forbidden |
| 404 vs 410 | 404 = unknown; 410 = existed, now permanently gone |
| 400 vs 422 | 400 = bad syntax/structure; 422 = valid syntax, invalid semantics |
| 301 vs 302 | 301 = permanent (cache it); 302 = temporary (don't cache) |
| 502 vs 504 | 502 = upstream returned bad response; 504 = upstream timed out |
Seeing These in Practice
When you're building an API, the JSON Formatter helps inspect response payloads — paste the JSON body alongside the status code to quickly read what the server returned.
For constructing and debugging URLs with query parameters, the URL Encoder handles encoding special characters correctly, which cuts down on the 400 Bad Request responses from malformed parameters.
For how status codes fit into the overall shape of a well-designed API, see REST API Design Best Practices. And for HTTP headers — the other half of the response envelope — read Understanding HTTP Headers.
The MDN HTTP status code reference is the authoritative documentation for edge cases and exact spec behavior.
Wrapping Up
Most applications only actively use about fifteen status codes. Use them consistently and precisely — a client should be able to handle your API's responses based on status code alone, without parsing the body first. Get the semantics right and you get easier monitoring, simpler client error handling, and faster debugging as a side effect.