HTTP Status Codes Every Developer Should Know — Complete Reference
HTTP status codes are the language servers use to tell clients what happened to their request. Knowing them — not just "200 = good, 404 = not found" — makes you dramatically faster at debugging APIs. This guide covers every class with real-world scenarios and when to use each.
5
classes: 1xx 2xx 3xx 4xx 5xx
63+
official HTTP status codes defined
~10
codes you'll use in 90% of cases
418
I'm a teapot — yes, it's a real code
The one rule to memorize
The first digit tells you the class: 1xx = informational, 2xx = success, 3xx = redirect, 4xx = client error, 5xx = server error. You can often diagnose the problem category just from the first digit — before you even look at the body.
2xx — Success Codes
The request was received, understood, and processed successfully.
200 OK
The standard success response. GET, PUT, PATCH succeeded.
Body: the requested resource or updated resource.
201 Created
A new resource was created. Use for successful POST requests.
Should include Location header with URL of new resource.
Location: /users/456
Body: the newly created resource { "id": 456, "name": "Alice" }
204 No Content
Success, but no body to return. Use for DELETE requests.
Also used for PUT/PATCH when you don't return the updated resource.
Never include a Content-Length or body with 204.
206 Partial Content
Used with Range requests (video streaming, large file downloads).
Content-Range: bytes 0-1023/146515
The client asked for a specific byte range of the resource.201 vs 200 for POST
3xx — Redirect Codes
The client needs to take additional action — usually following a different URL to get the resource.
301 Moved Permanently
Resource has moved to a new URL forever. Browsers cache this — future requests
skip the original URL entirely. Use when restructuring URLs permanently.
Location: /new-url
SEO impact: passes link equity to the new URL.
302 Found (Temporary Redirect)
Resource is temporarily at a different URL. Not cached by browsers.
Use for temporary maintenance pages, login redirects, A/B tests.
Method may change: POST → GET is common behavior (use 307 to prevent).
304 Not Modified
Client asked "did this change since I last fetched it?"
(via If-Modified-Since or If-None-Match/ETag headers)
Server says "no, your cached version is still current." No body.
Saves bandwidth and server CPU — client uses its local cache.
307 Temporary Redirect
Like 302, but the HTTP method MUST NOT change (POST stays POST).
Use 302 for redirecting GET requests, 307 for POST redirects.
308 Permanent Redirect
Like 301 (permanent, browser-cached), but method must not change.
POST stays POST across the redirect. Newer and less widely supported.4xx — Client Error Codes
The request is wrong — the problem is with what the client sent. 4xx errors are the client's fault. Good APIs return descriptive 4xx responses with a body explaining exactly what went wrong.
400 Bad Request
Request is malformed, missing required fields, or has invalid data.
Body should explain WHAT is wrong:
{ "error": "email is required", "field": "email" }
401 Unauthorized
Not authenticated. Client needs to provide or refresh credentials.
(Confusingly named — it actually means "unauthenticated", not "unauthorized")
WWW-Authenticate: Bearer realm="api"
Fix: provide a valid JWT, API key, or re-authenticate.
403 Forbidden
Authenticated, but not authorized. You don't have permission for this resource.
403 ≠ 401: with 403, re-authenticating with the SAME credentials won't help.
Fix: need different permissions, a different role, or contact an admin.
404 Not Found
Resource doesn't exist at this URL. Also used to hide existence of private
resources (return 404 instead of 403 to avoid revealing the resource exists).
405 Method Not Allowed
Wrong HTTP method for this endpoint. GET on a POST-only endpoint.
Must include Allow header: Allow: GET, POST
409 Conflict
Request conflicts with current state of the resource.
Examples: duplicate email on signup, version conflict in optimistic locking.
{ "error": "email already registered" }
410 Gone
Resource existed but has been permanently deleted. Unlike 404 (never existed).
Use when you want crawlers to deindex the URL — 410 tells them to stop crawling.
422 Unprocessable Entity
Syntactically valid JSON, but semantically wrong data.
Example: end_date before start_date, invalid enum value.
Preferred over 400 for validation errors in REST APIs.
429 Too Many Requests
Rate limit exceeded. Include headers showing the limit and when to retry.
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1699900800| Item | 401 Unauthorized | 403 Forbidden |
|---|---|---|
| Actual meaning | Not authenticated — who are you? | Authenticated but no permission — we know who you are |
| Fix | Provide or refresh credentials (log in) | Need a different role/permission — re-auth won't help |
| Re-authenticating helps? | Yes — log in again to get fresh credentials | No — same result with the same account |
| Real-world example | No JWT token in request, or token expired | Regular user trying to access /admin endpoint |
| Security note | Return 401 when credentials are missing | Return 404 (not 403) for private resources to avoid revealing they exist |
5xx — Server Error Codes
Something went wrong on the server. The request may have been perfectly valid — the server failed to fulfill it. 5xx errors are the server's fault, not the client's.
500 Internal Server Error
Generic server error. Something unexpectedly crashed or threw an exception.
Log the full error server-side with stack trace.
Return a safe, non-revealing message to clients:
{ "error": "Something went wrong.", "requestId": "req_abc123" }
501 Not Implemented
The server doesn't support this functionality.
Use for planned endpoints that aren't built yet, or unsupported HTTP methods.
502 Bad Gateway
The server acted as a proxy/gateway and received an invalid response from upstream.
Common: Nginx → Node.js crashed or returned garbage. CloudFlare → your origin.
503 Service Unavailable
Server is temporarily unable to handle requests.
Use for: maintenance mode, overload protection, graceful shutdown.
Retry-After: 30 (seconds)
504 Gateway Timeout
The gateway/proxy timed out waiting for a response from an upstream service.
Common: Nginx → slow backend, or microservice A → slow microservice B.502 vs 503 vs 504 — the gateway trilogy
What to Return from Your API — Decision Guide
GET /users/:id → user found
Return 200 OK with the user object in the body. Include Content-Type: application/json header.
POST /users → user created
Return 201 Created with the created user in the body AND a Location header: Location: /users/456. Never return 200 for resource creation.
DELETE /users/:id → deleted
Return 204 No Content with an empty body. Some APIs return 200 with a confirmation message — either is acceptable but 204 is more semantically correct.
POST /users with invalid data → validation error
Return 422 Unprocessable Entity (or 400) with a body describing each validation error by field: { "errors": [{ "field": "email", "message": "Invalid format" }] }.
GET /users/:id → not found
Return 404 Not Found with a brief message: { "error": "User not found" }. Never return 200 with null — that breaks client error handling.
GET /admin/users → regular user
Return 404 (not 403) if the resource is private — returning 403 confirms the resource exists. Return 403 only when the resource is publicly known to exist.
HTTP Status Code Quick Reference
2xx Success
200 OK · 201 Created · 204 No Content · 206 Partial Content (range requests, video streaming)
3xx Redirect
301 Permanent · 302 Temporary · 304 Not Modified (cached) · 307/308 Method-preserving redirects
4xx Client Error
400 Bad Request · 401 Unauthenticated · 403 Forbidden · 404 Not Found · 409 Conflict · 422 Validation · 429 Rate Limited
5xx Server Error
500 Internal Error · 502 Bad Gateway · 503 Unavailable · 504 Gateway Timeout
Designing Your Error Response Body
// ✅ Good error response — tells the client exactly what went wrong
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Must be a valid email address",
"received": "not-an-email"
},
{
"field": "age",
"message": "Must be a positive integer",
"received": -5
}
],
"requestId": "req_01JK4X9MABCDEF123456",
"documentation": "https://docs.api.com/errors/validation"
}
}
// ❌ Bad error response — client has no idea what to fix
{
"error": "Bad request"
}
// ❌ Never return 200 with an error body (anti-pattern)
// HTTP 200 OK ← client thinks it succeeded!
{
"success": false,
"error": "Something went wrong"
}