# 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.

Source: https://howhttpworks.com/guides/status-codes-overview
Last reviewed: 2026-10-05

> **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
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
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
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:**

```text
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
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
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
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
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:**

```text
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
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
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
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
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
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:**

```text
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
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
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
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
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
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
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:**

```text
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
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
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
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
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:**

```javascript
// 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:**

```javascript
// 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:**

```javascript
// 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:**

```javascript
// 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:**

```javascript
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:**

```javascript
// 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:**

```json
{
  "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:**

```javascript
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:**

```javascript
// 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:**

```javascript
// 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:**

```javascript
// 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:**

```javascript
// 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](https://howhttpworks.com/guides/request-lifecycle)**: How status codes fit into the complete request flow
- **[Headers and Caching](https://howhttpworks.com/guides/headers-and-caching)**: How status codes affect caching behavior
- **[Individual Status Codes](https://howhttpworks.com/status-codes/200)**: Detailed explanations of specific codes
- **[HTTP Methods](https://howhttpworks.com/methods/get)**: How different methods relate to expected status codes
- **[Error Handling](https://howhttpworks.com/headers/retry-after)**: 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.
