Every HTTP response includes a status code. Most developers know 200, 404, and 500. Beyond that, things get fuzzy — 401 vs 403, 502 vs 503, when to use 201 vs 200. Status codes are how your API communicates state to clients; using them well makes integration much easier.
This post is a practical reference: the five classes, the most-used codes, the codes commonly misused, and the patterns for which to use when.
The Five Classes
HTTP status codes are grouped by first digit:
- 1xx — Informational. Rare; mostly internal protocol signaling.
- 2xx — Success. The request worked.
- 3xx — Redirection. The client should look elsewhere.
- 4xx — Client error. The request was bad.
- 5xx — Server error. The server had a problem.
The class alone tells you the broad outcome. The specific code adds detail.
1xx Informational
Rarely seen by application developers. The notable one in 2026:
100 Continue
The client should continue the request. Used with Expect: 100-continue for large uploads — the server says “OK, send the body” before the client commits bandwidth.
101 Switching Protocols
Used in WebSocket upgrades.
103 Early Hints
Modern: the server sends preload hints before the actual response. Allows the browser to start fetching resources during server processing.
HTTP/2 103 Early Hints
Link: </styles.css>; rel=preload; as=style
HTTP/2 200 OK
...
103 is the modern replacement for HTTP/2 Server Push (which was deprecated). See HTTP/2 vs HTTP/3.
2xx Success
The good outcomes.
200 OK
The default success. The request worked, here’s the response.
201 Created
A new resource was created. Used after POST that creates something.
POST /users
HTTP/1.1 201 Created
Location: /users/12345
{ "id": 12345, "name": "Alice" }
The Location header points to the newly created resource.
202 Accepted
The request was accepted but processing is asynchronous. Used for queued/long-running operations.
204 No Content
The request worked; the response body is intentionally empty. Used for DELETE confirmations and some PUT updates.
206 Partial Content
Used for range requests (resumable downloads, video streaming).
Common mistake
Returning 200 with an error body. If something went wrong, use a 4xx or 5xx code. Clients should be able to check the status code first; the body is the detail.
3xx Redirection
The “look somewhere else” responses.
301 Moved Permanently
The resource has permanently moved. Browsers and search engines update their cached references.
302 Found (originally “Moved Temporarily”)
Temporary redirect. The resource is at this URL right now but might move back.
303 See Other
“GET the URL in the Location header instead.” Often used after POST-redirect-GET to prevent re-submission on refresh.
304 Not Modified
The client’s cached version is still valid. Body is empty. Used with conditional requests (If-Modified-Since, If-None-Match).
307 Temporary Redirect
Like 302, but the client must use the same method. (302’s behavior with non-GET requests was historically inconsistent.)
308 Permanent Redirect
Like 301, but client must use the same method.
When to use which
- 301/308 — Permanent move (URL restructured, domain changed).
- 302/307 — Temporary redirect (maintenance, A/B test, geo-routing).
- 303 — After form submission, redirect to a result page.
- 304 — Caching responses.
4xx Client Error
The request had a problem.
400 Bad Request
The request was malformed. Use for parse errors, validation failures.
401 Unauthorized
Authentication is required and either missing or invalid. The client should provide credentials.
(Yes, “Unauthorized” is mis-named — it means “not authenticated.” This is one of HTTP’s historical quirks.)
403 Forbidden
The client is authenticated but doesn’t have permission. Different from 401: 401 says “log in”; 403 says “you’re logged in but can’t do this.”
404 Not Found
The resource doesn’t exist. The most-known code.
405 Method Not Allowed
The URL exists, but not for this method. Used when GET works on /users but POST doesn’t (e.g., it’s read-only).
406 Not Acceptable
The server can’t produce a response matching the client’s Accept headers.
408 Request Timeout
The client took too long.
409 Conflict
The request conflicts with current server state. Used for concurrent edit conflicts, duplicate creates.
410 Gone
Like 404, but specifically “this resource used to exist but is permanently gone.” Search engines treat this differently.
412 Precondition Failed
A conditional header (If-Match, etc.) didn’t pass. Used for optimistic concurrency control.
413 Payload Too Large
The request body is too big.
415 Unsupported Media Type
The server doesn’t understand the request’s content type.
422 Unprocessable Entity
The request is well-formed but semantically wrong. Often used for validation errors in JSON APIs.
429 Too Many Requests
Rate limit exceeded. Should include Retry-After header.
For rate limiting patterns, see rate limiting algorithms.
Common mistakes
- 401 when you meant 403. Auth missing vs auth insufficient.
- 403 for everything. Don’t lump auth failures and permission failures together.
- 200 with error body instead of 4xx. The status code is the canonical signal.
- 400 for everything 4xx. Be specific — 422 for validation, 404 for missing, etc.
5xx Server Error
Something on the server side went wrong.
500 Internal Server Error
The generic “something broke.” Usually unhandled exception or unexpected condition.
501 Not Implemented
The server doesn’t recognize the request method.
502 Bad Gateway
The server (acting as gateway/proxy) got an invalid response from upstream. Usually means the application server returned garbage or crashed.
503 Service Unavailable
The server is temporarily unable to handle the request (overloaded, maintenance). Should include Retry-After.
504 Gateway Timeout
The gateway didn’t get a timely response from upstream. Usually means the application server is slow or hung.
507 Insufficient Storage
Used for WebDAV; rarely seen in REST APIs.
Common mistake
Returning 500 when 503 is correct. 500 implies a bug; 503 implies a temporary capacity issue. Different signals to clients and monitoring tools.
Status Codes for REST APIs
A typical API style guide:
- GET /resources — 200 with list, or 404 if collection itself missing.
- GET /resources/:id — 200 with object, 404 if missing.
- POST /resources — 201 + Location header, or 400/422 on bad input.
- PUT /resources/:id — 200 if updated, 201 if created, 204 if no body returned.
- DELETE /resources/:id — 204 on success, 404 if missing, 410 if previously deleted.
- PATCH /resources/:id — 200 with updated object, or 204 if no body.
For errors:
- 400 — Bad syntax (can’t parse the body).
- 401 — Need authentication.
- 403 — Authenticated but unauthorized.
- 404 — Resource not found.
- 409 — Conflict (duplicate, concurrent edit).
- 422 — Validation failed.
- 429 — Rate limit.
- 500 — Generic server error.
- 503 — Service down/overloaded.
Caching Implications
Status codes affect caching:
- 200, 301, 410 — Cacheable by default.
- 302, 307 — Cacheable only with explicit Cache-Control.
- 404 — Cacheable, briefly, by default.
- 5xx — Generally not cached.
For 304 specifically: returning 304 effectively says “use the cached version you had.” Don’t include a body; the response should be header-only.
Error Bodies
Status code tells you the class of error; the body should tell you the detail:
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"error": "validation_failed",
"fields": [
{ "field": "email", "code": "invalid_format" },
{ "field": "age", "code": "must_be_positive" }
]
}
Standards for this body shape vary. RFC 7807 (“Problem Details for HTTP APIs”) proposes a standard:
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json
{
"type": "https://example.com/errors/validation",
"title": "Validation failed",
"status": 422,
"errors": [...]
}
Whether or not you adopt RFC 7807, be consistent within your API. Clients should be able to predict the shape.
CORS and Status Codes
A quirk: CORS errors don’t have status codes. The browser blocks the response entirely; JavaScript sees a network error.
This makes CORS-related issues confusing — your server returned 500, but JavaScript shows “CORS error.” Always include CORS headers on all responses, including errors. See CORS explained.
A Few Less Common but Useful
418 I’m a teapot
Joke status code from RFC 2324 (“Hyper Text Coffee Pot Control Protocol”). Some APIs use it for “this method shouldn’t be called” gags. Not a real production signal.
451 Unavailable For Legal Reasons
“This content is blocked due to legal requirements.” Named after Fahrenheit 451. Used for content geoblocked due to legal reasons (regional copyright, censorship). See geofencing 101.
499 Client Closed Request
NGINX-specific. Client disconnected before the server could respond.
TL;DR
- 2xx success, 3xx redirect, 4xx client error, 5xx server error.
- Be specific. 422 for validation > 400 generic.
- 401 vs 403: unauthenticated vs unauthorized.
- 301/308 permanent, 302/307 temporary, 303 after POST.
- 204 for “no body” success (DELETE, PUT updates).
- 429 with Retry-After for rate limiting.
- 503 with Retry-After for capacity issues.
- Always include CORS headers on errors, not just successes.
- Body has the detail; status code has the class.
HTTP status codes are one of those areas where small attention to detail pays off forever. APIs that use status codes well are easier to integrate, easier to debug, and easier to maintain. For related HTTP topics, see CORS explained; for the modern transport, HTTP/2 vs HTTP/3; for rate-limiting patterns that use 429, rate limiting algorithms.