HTTP Status Codes: What Your Server Is Really Telling the Client
Every HTTP response includes a three-digit status code. Get them right and your API is intuitive. Get them wrong and developers spend hours debugging why the client retries on a 200 that should've been a 400.
The Five Classes
| Range | Category | Meaning |
|---|
| 1xx | Informational | Request received, continuing |
| 2xx | Success | Request accepted and processed |
| 3xx | Redirection | Client must take additional action |
| 4xx | Client Error | Request has a problem |
| 5xx | Server Error | Server failed to fulfill valid request |
Success Codes (2xx)
| Code | Name | When to Use |
|---|
| 200 | OK | GET succeeded, PUT/PATCH updated successfully |
| 201 | Created | POST created a new resource. Include Location header with the new resource URL. |
| 202 | Accepted | Request accepted for async processing (e.g., queued job). Not done yet. |
| 204 | No Content | DELETE succeeded, nothing to return. Also good for PUT with no response body. |
// Express.js examples
app.post('/users', (req, res) => {
const user = createUser(req.body);
res.status(201).json(user);
});
app.delete('/users/:id', (req, res) => {
deleteUser(req.params.id);
res.status(204).send();
});
app.post('/reports', (req, res) => {
queueReportGeneration(req.body);
res.status(202).json({ message: 'Report is being generated' });
});
Redirection Codes (3xx)
| Code | Name | When to Use |
|---|
| 301 | Moved Permanently | Resource URL changed forever. Browsers cache this. Use for domain migrations. |
| 302 | Found | Temporary redirect. Browser uses GET for the redirect (even if original was POST). |
| 304 | Not Modified | Conditional GET — resource hasn't changed since the cached version. |
| 307 | Temporary Redirect | Like 302, but preserves the HTTP method. POST stays POST. |
| 308 | Permanent Redirect | Like 301, but preserves the HTTP method. |
Common mistake: Using 302 when you mean 301. If the URL change is permanent (moved to a new domain, changed URL structure), use 301 so search engines update their index.
Client Error Codes (4xx)
| Code | Name | When to Use |
|---|
| 400 | Bad Request | Malformed JSON, missing required fields, invalid data types |
| 401 | Unauthorized | No authentication provided or token is invalid. Misleading name — it means "unauthenticated". |
| 403 | Forbidden | Authenticated but doesn't have permission. The user is known, but can't access this resource. |
| 404 | Not Found | Resource doesn't exist at this URL. |
| 405 | Method Not Allowed | Endpoint exists, but not for this HTTP method (e.g., POST to a GET-only endpoint). |
| 409 | Conflict | Conflicts with current state — duplicate email, version mismatch, concurrent edit. |
| 422 | Unprocessable Entity | JSON is valid, but the data doesn't pass validation (e.g., email format wrong). |
| 429 | Too Many Requests | Rate limited. Include Retry-After header. |
// 400 vs 422 — both are valid, pick one convention
// 400: request is structurally wrong (malformed JSON)
// 422: request is structurally fine, but data is invalid
app.post('/users', (req, res) => {
if (!req.body.email) {
return res.status(422).json({
error: 'Validation failed',
details: [{ field: 'email', message: 'Email is required' }]
});
}
});
Server Error Codes (5xx)
| Code | Name | When to Use |
|---|
| 500 | Internal Server Error | Generic "something broke." Don't leak stack traces to clients. |
| 502 | Bad Gateway | Reverse proxy (Nginx) couldn't connect to the upstream server (your app crashed). |
| 503 | Service Unavailable | Server is temporarily down — maintenance, overloaded. Include Retry-After. |
| 504 | Gateway Timeout | Reverse proxy timed out waiting for your app to respond. |
Key principle: 4xx means the client needs to fix something. 5xx means the server needs to fix something. If the client can never succeed by changing their request, it's a 5xx.
Decision Tree
Is the request valid?
├── No → Is it a structural problem?
│ ├── Yes → 400 Bad Request
│ └── No → 422 Unprocessable Entity
├── Yes → Is the user authenticated?
│ ├── No → 401 Unauthorized
│ └── Yes → Is the user authorized?
│ ├── No → 403 Forbidden
│ └── Yes → Does the resource exist?
│ ├── No → 404 Not Found
│ └── Yes → Was it processed successfully?
│ ├── Yes → 200/201/204
│ └── No → 500 Internal Server Error
Quick reference: HTTP Status Codes — searchable table with descriptions, categories, and examples for every code.