Guide
HTTP Status Codes: Full List with Meanings
Every HTTP status code class from 1xx to 5xx with what each code means and when to return it, including the ones you meet most: 200, 301, 304, 404 and 500.
On this page
- Introduction
- The Five Status Code Classes
- 1xx Informational Responses
- 2xx Success Responses
- 3xx Redirection Responses
- 4xx Client Error Responses
- 5xx Server Error Responses
- Status Code Selection Patterns
- RESTful API Patterns
- Error Handling Patterns
- Authentication and Authorization Patterns
- Getting Status Codes right
- Consistency Guidelines
- Client-Side Handling
- Monitoring and Debugging
- Common Anti-Patterns
- Incorrect Status Code Usage
- Missing Error Context
- Related Concepts
TL;DR: The first digit of a status code tells you what happened: 1xx means “still working”, 2xx means it worked, 3xx means look elsewhere (a redirect or your cached copy), 4xx means the request was wrong, and 5xx means the server failed. In your own API, return 201 for creates, 202 for queued work, 204 when there’s nothing to send back, and a real 4xx or 5xx for errors instead of 200 with an error in the body.
Every HTTP response starts with a three-digit status code. It’s how the server tells the client whether the request succeeded, failed, or needs another step. Reading it correctly is the first move when you debug a failing request, and picking it correctly is part of designing your own API.
This guide walks through the five classes, gives you a decision tree for each, and ends with server and client patterns you can copy. The HTTP responses below are trimmed examples, not captures from a specific server.
Introduction
The codes are standardized, so a 404 means the same thing to nginx, Chrome, curl and your mobile app. The first digit carries the category, and the last two pick a specific outcome within it. A client that doesn’t recognize a code still knows how to treat its class.
Different consumers read the status for different reasons. Browsers use it to decide whether to follow a redirect, use a cached copy or show an error page. Search engines use it to decide what to index. Your client code branches on it, and your monitoring counts it. Getting it wrong shows up everywhere: an error page served as 200 gets indexed, and a 500 that should have been a 400 wakes up whoever is on call.
Each class has a distinct job. Once you know which class an outcome belongs to, picking the specific code is usually a short decision.
The Five Status Code Classes
1xx Informational Responses
A 1xx response says the server has the request and is still working on it. It’s an interim response: a final status always follows.
Key Characteristics:
- Provisional responses during request processing
- Client should wait for final response
- Rarely used in typical web applications
- More common in streaming and long-running operations
Common 1xx Codes:
100 Continue
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1247
{"status": "upload complete"}
The server sends this when a client asked with Expect: 100-continue before uploading a large body. The client waits for the go-ahead, sends the body, then gets the final status.
101 Switching Protocols
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
The server agrees to switch protocols on this connection, most often from HTTP/1.1 to WebSocket. The cleartext HTTP/1.1-to-HTTP/2 upgrade (h2c) also used 101, but RFC 9113 deprecated it; HTTP/2 is negotiated through TLS ALPN instead.
102 Processing (WebDAV)
HTTP/1.1 102 Processing
Tells the client the server is still working on a long request, so it shouldn’t time out yet.
2xx Success Responses
A 2xx means the server received, understood and accepted the request. The specific code tells the client what kind of success it got.
Decision Framework for 2xx Codes:
Request successful?
├─ Yes → Was resource created?
│ ├─ Yes → 201 Created
│ └─ No → Was content returned?
│ ├─ Yes → 200 OK
│ ├─ No content → 204 No Content
│ └─ Accepted for processing → 202 Accepted
└─ No → Use 4xx or 5xx
Common 2xx Codes:
200 OK - Standard Success
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 156
{
"users": [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
]
}
The everyday success response. The request worked and the body carries the result.
201 Created - Resource Creation
HTTP/1.1 201 Created
Location: /api/users/123
Content-Type: application/json
{
"id": 123,
"name": "Charlie",
"createdAt": "2026-01-18T10:30:00Z"
}
The request created a new resource. Send a Location header that points to it.
202 Accepted - Asynchronous Processing
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"jobId": "job_abc123",
"status": "processing",
"estimatedCompletion": "2026-01-18T10:35:00Z"
}
The server accepted the request but hasn’t finished the work. Use it for background jobs, and give the client a job ID or status URL to poll.
204 No Content - Successful with No Response Body
HTTP/1.1 204 No Content
Cache-Control: no-cache
The request worked and there’s nothing to send back. You’ll see it on DELETEs and on updates that don’t echo the resource.
3xx Redirection Responses
A 3xx means the client has to do something else to finish. Usually that’s following a Location header to a new URL; with 304 it means reusing the copy it already has.
Redirection Decision Tree:
Resource moved?
├─ Permanently → 301 Moved Permanently
├─ Temporarily → 302 Found or 307 Temporary Redirect
├─ Not modified → 304 Not Modified
└─ Multiple choices → 300 Multiple Choices
Common 3xx Codes:
301 Moved Permanently - Permanent Redirect
HTTP/1.1 301 Moved Permanently
Location: https://newdomain.com/api/users
Cache-Control: max-age=31536000
<html>
<body>Resource has moved to <a href="https://newdomain.com/api/users">new location</a></body>
</html>
The resource has a new permanent URL. Clients and caches can remember the redirect, and search engines move their index entries to the new URL.
302 Found - Temporary Redirect
HTTP/1.1 302 Found
Location: /login?redirect=/dashboard
Cache-Control: no-cache
The resource is temporarily somewhere else. Keep using the original URL for future requests.
304 Not Modified - Cached Content Valid
HTTP/1.1 304 Not Modified
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Cache-Control: max-age=300
The client’s cached copy is still current, so the server sends no body and the client uses what it has.
307 Temporary Redirect - Method Preserved
HTTP/1.1 307 Temporary Redirect
Location: /api/v2/users
Like 302, but the client must repeat the request with the same method and body. A POST stays a POST.
308 Permanent Redirect - Method Preserved
HTTP/1.1 308 Permanent Redirect
Location: https://api.example.com/v2/users
Like 301, but the method and body stay the same on the redirected request.
4xx Client Error Responses
A 4xx means the problem is on the client’s side: bad syntax, missing credentials, a resource that isn’t there, or a request the server won’t fulfill. Sending the same request again unchanged usually gets the same answer; the client has to fix something first.
Client Error Decision Framework:
Request valid?
├─ No → Malformed syntax → 400 Bad Request
├─ Yes → Authorized?
│ ├─ No → Authentication required → 401 Unauthorized
│ ├─ Forbidden → 403 Forbidden
│ └─ Yes → Resource exists?
│ ├─ No → 404 Not Found
│ ├─ Yes → Method allowed?
│ │ ├─ No → 405 Method Not Allowed
│ │ └─ Yes → Content acceptable?
│ │ ├─ No → 406 Not Acceptable
│ │ └─ Yes → Rate limited?
│ │ ├─ Yes → 429 Too Many Requests
│ │ └─ No → Process request
└─ Validation failed → 422 Unprocessable Entity
Common 4xx Codes:
400 Bad Request - Malformed Request
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "Invalid JSON syntax",
"details": "Unexpected token '}' at position 45",
"code": "INVALID_JSON"
}
The server can’t parse or understand the request, such as malformed JSON.
401 Unauthorized - Authentication Required
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
Content-Type: application/json
{
"error": "Authentication required",
"message": "Please provide a valid access token"
}
The request has no valid credentials. Send a WWW-Authenticate header so the client knows how to authenticate.
403 Forbidden - Access Denied
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"error": "Insufficient permissions",
"message": "Admin access required for this operation"
}
The server knows what you asked for and won’t allow it. Repeating the request with the same credentials won’t change the answer.
404 Not Found - Resource Missing
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"error": "Resource not found",
"message": "User with ID 999 does not exist"
}
There’s nothing at this URL, or the server won’t say whether there is.
422 Unprocessable Entity - Validation Failed
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"error": "Validation failed",
"details": [
{"field": "email", "message": "Invalid email format"},
{"field": "age", "message": "Must be between 18 and 120"}
]
}
The request parses fine, but the content doesn’t make sense, such as a field that fails validation.
429 Too Many Requests - Rate Limited
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1642518000
{
"error": "Rate limit exceeded",
"message": "Try again in 60 seconds"
}
The client hit a rate limit. Send Retry-After so it knows when to try again.
5xx Server Error Responses
A 5xx means the server failed to handle a request that looked valid. The client can’t fix it by changing the request; it can wait and retry, if retrying is safe for that method.
Server Error Decision Framework:
Server error occurred?
├─ Generic error → 500 Internal Server Error
├─ Feature not implemented → 501 Not Implemented
├─ Upstream server error → 502 Bad Gateway
├─ Service unavailable → 503 Service Unavailable
├─ Upstream timeout → 504 Gateway Timeout
└─ HTTP version unsupported → 505 HTTP Version Not Supported
Common 5xx Codes:
500 Internal Server Error - Generic Server Error
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{
"error": "Internal server error",
"message": "An unexpected error occurred",
"requestId": "req_abc123"
}
The catch-all for an unexpected failure. Include a request ID so you can find it in your logs.
502 Bad Gateway - Upstream Server Error
HTTP/1.1 502 Bad Gateway
Content-Type: application/json
{
"error": "Bad gateway",
"message": "Database connection failed"
}
A gateway or proxy got an invalid response from the server behind it.
503 Service Unavailable - Temporary Unavailability
HTTP/1.1 503 Service Unavailable
Retry-After: 120
Content-Type: application/json
{
"error": "Service unavailable",
"message": "Server is temporarily overloaded"
}
The server can’t take the request right now, typically during maintenance or overload. Add Retry-After when you know how long.
504 Gateway Timeout - Upstream Timeout
HTTP/1.1 504 Gateway Timeout
Content-Type: application/json
{
"error": "Gateway timeout",
"message": "Upstream server did not respond in time"
}
A gateway or proxy gave up waiting for the server behind it.
Status Code Selection Patterns
RESTful API Patterns
Resource Creation:
// POST /api/users
if (validationErrors.length > 0) {
return res.status(422).json({ errors: validationErrors })
}
if (userAlreadyExists) {
return res.status(409).json({ error: 'User already exists' })
}
const user = await createUser(userData)
return res.status(201).json(user)
Resource Retrieval:
// GET /api/users/:id
const user = await findUser(userId)
if (!user) {
return res.status(404).json({ error: 'User not found' })
}
return res.status(200).json(user)
Resource Updates:
// PUT /api/users/:id
const user = await findUser(userId)
if (!user) {
return res.status(404).json({ error: 'User not found' })
}
if (validationErrors.length > 0) {
return res.status(422).json({ errors: validationErrors })
}
const updatedUser = await updateUser(userId, userData)
return res.status(200).json(updatedUser)
Resource Deletion:
// DELETE /api/users/:id
const user = await findUser(userId)
if (!user) {
return res.status(404).json({ error: 'User not found' })
}
await deleteUser(userId)
return res.status(204).send()
Error Handling Patterns
Graceful Degradation:
try {
const result = await processRequest(data)
return res.status(200).json(result)
} catch (error) {
if (error instanceof ValidationError) {
return res.status(422).json({ error: error.message })
}
if (error instanceof NotFoundError) {
return res.status(404).json({ error: error.message })
}
// Log unexpected errors
logger.error('Unexpected error:', error)
return res.status(500).json({
error: 'Internal server error',
requestId: req.id
})
}
Authentication and Authorization Patterns
Authentication Flow:
// Check if token exists
if (!token) {
return res.status(401).json({
error: 'Authentication required',
message: 'Please provide an access token'
})
}
// Validate token
const user = await validateToken(token)
if (!user) {
return res.status(401).json({
error: 'Invalid token',
message: 'Please log in again'
})
}
// Check permissions
if (!user.hasPermission(requiredPermission)) {
return res.status(403).json({
error: 'Insufficient permissions',
message: 'Admin access required'
})
}
Getting Status Codes right
Consistency Guidelines
Use Standard Codes:
- Use the well-known codes; clients and tools already understand them
- Reach for a rarer code only when it tells the client something the common ones can’t
- Document any non-standard code your API returns
Provide Helpful Error Messages:
{
"error": "Validation failed",
"message": "The request contains invalid data",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Email must be a valid email address"
}
],
"requestId": "req_abc123"
}
Client-Side Handling
Branch on the class first, then handle the specific codes your app cares about. fetch follows redirects for you by default, so the 3xx branch below mainly matters for clients that don’t.
Status Code Categories:
function handleResponse(response) {
const status = response.status
if (status >= 200 && status < 300) {
// Success - process response data
return response.json()
}
if (status >= 300 && status < 400) {
// Redirection - follow redirect or update URL
window.location.href = response.headers.get('Location')
}
if (status >= 400 && status < 500) {
// Client error - show user-friendly error message
throw new ClientError('Please check your request and try again')
}
if (status >= 500) {
// Server error - show generic error, maybe retry
throw new ServerError('Something went wrong. Please try again later')
}
}
Monitoring and Debugging
Status Code Metrics:
// Track status code distribution
const statusCodeCounts = {
'2xx': 0, // Success rate
'3xx': 0, // Redirect rate
'4xx': 0, // Client error rate
'5xx': 0 // Server error rate
}
// Alert on high error rates
if (statusCodeCounts['5xx'] / totalRequests > 0.01) {
alert('High server error rate detected')
}
Common Anti-Patterns
Incorrect Status Code Usage
Don’t Use 200 for Errors:
// Bad: Error with 200 status
res.status(200).json({
success: false,
error: 'User not found'
})
// Good: Proper error status
res.status(404).json({
error: 'User not found'
})
Don’t Use Generic Codes:
// Bad: Generic 400 for validation errors
res.status(400).json({ error: 'Invalid data' })
// Good: Specific 422 for validation errors
res.status(422).json({
error: 'Validation failed',
details: validationErrors
})
Missing Error Context
Provide Actionable Information:
// Bad: Vague error message
res.status(403).json({ error: 'Forbidden' })
// Good: Specific guidance
res.status(403).json({
error: 'Insufficient permissions',
message: 'This operation requires admin privileges',
requiredRole: 'admin',
currentRole: 'user'
})
Related Concepts
Status codes connect to most of the rest of HTTP:
- Request Lifecycle: How status codes fit into the complete request flow
- Headers and Caching: How status codes affect caching behavior
- Individual Status Codes: Detailed explanations of specific codes
- HTTP Methods: How different methods relate to expected status codes
- Error Handling: Headers that provide additional error context
If you’re designing an API, start with the codes most clients already handle (200, 201, 204, 400, 401, 403, 404, 500) and add more specific ones when they tell the client something it can act on.
Check your understanding
Choose status codes from real product scenarios
Use the status picker to compare outcomes for synchronous success, background processing, and validation failure.
- Answer the flow once for a successful resource creation and note why it lands on `201 Created`.
- Run it again for a job that was accepted but is still running and compare the result with `202 Accepted`.
- Run a third pass for invalid request data and note why the tool steers you away from generic success responses.
Look for: You should see that good status-code choices come from request outcome and client expectations, not from habit.
Why it matters: The right status code is part of the contract your client code relies on.
Choose the correct async response
A client uploads a video, and your API queues transcoding that will finish later. Which response is the best default?
Frequently asked questions
What are HTTP status codes?
Status codes are 3-digit numbers indicating the result of an HTTP request. They are grouped: 1xx informational, 2xx success, 3xx redirect, 4xx client error, 5xx server error.
What are the most common status codes?
200 OK (success), 301/302 (redirect), 400 (bad request), 401 (unauthorized), 403 (forbidden), 404 (not found), 500 (server error), 503 (service unavailable).
What is the difference between 4xx and 5xx errors?
4xx errors are client errors (bad request, not found). 5xx errors are server errors (server crashed, overloaded). 4xx means fix your request; 5xx means wait or contact support.
How do I handle status codes in my application?
Check the status code before processing the response. Handle success (2xx), follow redirects (3xx), show user-friendly errors for 4xx, and retry or alert for 5xx.
Sources
Related
Request and Response Lifecycle
Learn how HTTP requests travel from browser to server and back. Understand DNS resolution, TCP connections, request/response flow, and the complete lifecycle.
HTTP Sessions and State Management Explained
Learn how to manage user state and sessions in stateless HTTP applications using cookies, tokens, and server-side storage.
Content Negotiation: Accept, Languages and Caches
Implement HTTP content negotiation with Accept headers, q-values, Vary and 406 responses, with Express, Django and nginx examples and language SEO guidance.
WebSockets over HTTP: Handshakes, Proxies and Auth
Trace a WebSocket HTTP handshake, verify Sec-WebSocket-Accept, configure nginx, secure browser authentication, and debug idle disconnects and close codes.