HTTP Status Codes: The Complete Developer Reference
Every time your browser loads a page or your app calls an API, the server sends back a three-digit number that tells you exactly what happened. These HTTP status codes are the universal language of the web — and understanding them is essential for building, consuming, and debugging APIs.
This guide covers every status code you'll encounter in practice, explains the subtle differences that trip developers up (like 401 vs 403), and shows you how to choose the right codes for your own APIs.
What Are HTTP Status Codes?
HTTP status codes are three-digit integers included in the response line of every HTTP response. When a client (browser, mobile app, CLI tool) sends a request to a server, the server always replies with a status code that indicates the outcome.
A typical HTTP response starts like this:
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 26 Mar 2026 12:00:00 GMT
{"message": "Hello, world"}
The first digit of the status code defines its class. There are five classes:
- 1xx — Informational: the request was received, processing continues
- 2xx — Success: the request was received, understood, and accepted
- 3xx — Redirection: further action is needed to complete the request
- 4xx — Client Error: the request contains bad syntax or cannot be fulfilled
- 5xx — Server Error: the server failed to fulfil a valid request
Knowing which class a code belongs to immediately tells you where the problem lies — the client side, the server side, or nowhere at all.
1xx Informational
The 1xx codes are interim responses. You rarely see them in application-level code, but they play an important role under the hood.
100 Continue
When a client needs to send a large request body (like a file upload), it can first send the headers with Expect: 100-continue. The server responds with 100 Continue to signal "go ahead and send the body" — or rejects the request early with a 4xx code, saving bandwidth.
# Client sends headers first
POST /upload HTTP/1.1
Content-Length: 52428800
Expect: 100-continue
# Server replies: "go ahead"
HTTP/1.1 100 Continue
# Client then sends the 50MB body
101 Switching Protocols
Used when the client asks to switch from HTTP to a different protocol. The most common case is upgrading to WebSockets:
GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
After this handshake, the connection is no longer HTTP — it's a full-duplex WebSocket channel.
103 Early Hints
A newer code that lets the server send preliminary headers (like Link headers for preloading CSS or fonts) while it's still preparing the final response. Browsers can start fetching those resources early, improving page load times.
2xx Success
The codes you want to see. A 2xx response means the request was successfully received, understood, and accepted.
200 OK
The most common status code. The request succeeded and the response body contains the requested resource. Use 200 for successful GET requests that return data, and for PUT/PATCH requests that return the updated resource.
201 Created
The request succeeded and a new resource was created as a result. This is the correct code for successful POST requests that create something. The response should include a Location header pointing to the new resource:
POST /api/users HTTP/1.1
Content-Type: application/json
{"name": "Alice", "email": "alice@example.com"}
HTTP/1.1 201 Created
Location: /api/users/42
Content-Type: application/json
{"id": 42, "name": "Alice", "email": "alice@example.com"}
204 No Content
The request succeeded but there's nothing to send back in the response body. This is the ideal response for successful DELETE requests and PUT/PATCH requests where the client doesn't need the updated resource echoed back.
206 Partial Content
The server is delivering only part of the resource because the client sent a Range header. This is how video streaming, resumable downloads, and PDF viewers work — the client requests specific byte ranges instead of the entire file.
GET /video.mp4 HTTP/1.1
Range: bytes=0-1048575
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1048575/52428800
Content-Length: 1048576
Tip: When building APIs, use 201 for creation, 204 for deletion, and 200 for everything else that succeeds. This precision makes your API self-documenting.
3xx Redirection
Redirection codes tell the client that the resource has moved and they need to look elsewhere. The differences between them are subtle but important.
301 Moved Permanently
The resource has permanently moved to a new URL. Search engines will transfer link equity to the new URL. Browsers and clients may change the HTTP method to GET when following the redirect — this is an important caveat.
302 Found (Temporary Redirect)
The resource is temporarily at a different URL. Like 301, clients may change the method to GET when following. Despite the specification, most browsers always change POST to GET on a 302 redirect.
307 Temporary Redirect
Like 302, but the client must not change the HTTP method. If the original request was a POST, the redirect must also be a POST. Use this when method preservation matters.
308 Permanent Redirect
Like 301, but the client must not change the HTTP method. This is the permanent equivalent of 307.
| Code | Permanent? | Method Preserved? | Use Case |
|---|---|---|---|
301 | Yes | No (may change to GET) | Page moved, SEO redirect |
302 | No | No (may change to GET) | Temporary redirect (legacy) |
307 | No | Yes | Temporary redirect for APIs |
308 | Yes | Yes | Permanent redirect for APIs |
304 Not Modified
This isn't really a "redirect" — it's a caching optimisation. When the client sends conditional headers (If-None-Match or If-Modified-Since) and the resource hasn't changed, the server responds with 304 and an empty body. The client uses its cached copy, saving bandwidth and latency.
# First request — server includes ETag
GET /api/config HTTP/1.1
HTTP/1.1 200 OK
ETag: "abc123"
# Second request — client sends the ETag back
GET /api/config HTTP/1.1
If-None-Match: "abc123"
# Resource unchanged — use your cache
HTTP/1.1 304 Not Modified
Warning: Using 301 for API endpoint redirects can silently convert POST requests to GET, losing the request body entirely. Use 307 or 308 for API redirects where the method matters.
4xx Client Errors
The 4xx codes mean the client sent something wrong. These are the codes you'll encounter most often while debugging.
400 Bad Request
The server cannot process the request due to malformed syntax — invalid JSON, missing required fields, wrong data types. This is the catch-all for "your request doesn't make sense."
# Malformed JSON
POST /api/users HTTP/1.1
Content-Type: application/json
{"name": "Alice", email: broken}
HTTP/1.1 400 Bad Request
{"error": "Invalid JSON in request body"}
401 Unauthorized vs 403 Forbidden
This is one of the most commonly confused pairs in HTTP. Despite the name, 401 is about authentication, not authorisation:
- 401 Unauthorized — "I don't know who you are." The request lacks valid authentication credentials. The client should authenticate (log in) and try again. The response should include a
WWW-Authenticateheader. - 403 Forbidden — "I know who you are, but you're not allowed." The server understood the request and the client's identity, but the client doesn't have permission to access the resource. Re-authenticating won't help.
Tip: Think of 401 as "please log in" and 403 as "you don't have access." A missing or expired JWT token is 401. A valid token for a user who isn't an admin trying to access the admin panel is 403.
404 Not Found
The server cannot find the requested resource. This could mean the URL is wrong, the resource was deleted, or the endpoint doesn't exist. Some APIs also return 404 instead of 403 to avoid leaking information about the existence of resources.
405 Method Not Allowed
The HTTP method is not supported for this URL. For example, sending a DELETE request to an endpoint that only accepts GET. The response should include an Allow header listing the valid methods:
DELETE /api/reports HTTP/1.1
HTTP/1.1 405 Method Not Allowed
Allow: GET, POST
409 Conflict
The request conflicts with the current state of the resource. Common examples include trying to create a user with an email that already exists, or updating a resource with a stale version (optimistic concurrency control).
422 Unprocessable Entity
The server understands the request syntax (it's valid JSON), but the content is semantically invalid. For example, the JSON is well-formed but an email field contains "not-an-email". Some APIs prefer 422 over 400 to distinguish between syntactically broken requests and semantically invalid ones.
429 Too Many Requests
The client has sent too many requests in a given time window (rate limiting). The response should include a Retry-After header telling the client when they can try again:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1711454460
{"error": "Rate limit exceeded. Try again in 60 seconds."}
5xx Server Errors
The 5xx codes mean the server knows the request was valid but something went wrong on its end. These are the codes that wake you up at 3 a.m.
500 Internal Server Error
The generic "something broke" response. An unhandled exception, a null pointer, a failed database query — anything that the server didn't anticipate. If you're building an API, never expose raw stack traces in a 500 response. Log the details internally and return a safe error message to the client.
502 Bad Gateway
The server, acting as a gateway or proxy (like Nginx or a load balancer), received an invalid response from the upstream server. This usually means your application server crashed or returned something malformed. If you see 502 errors, check the upstream application, not the proxy itself.
503 Service Unavailable
The server is temporarily unable to handle the request, usually due to maintenance or overload. This is the right code for planned downtime. Include a Retry-After header so clients know when to come back:
HTTP/1.1 503 Service Unavailable
Retry-After: 3600
Content-Type: application/json
{"error": "Scheduled maintenance. Back in approximately 1 hour."}
504 Gateway Timeout
The gateway or proxy timed out waiting for a response from the upstream server. This often means a slow database query, an overloaded application server, or a network issue between services. Check your upstream server's response times and consider increasing timeout thresholds or optimising slow operations.
Warning: Never return a 200 OK with an error message in the body. This is a common anti-pattern that breaks error handling, caching, and monitoring. Use the correct 4xx or 5xx code so clients, proxies, and observability tools can do their jobs.
Status Codes in REST API Design
Choosing the right status code makes your API predictable and self-documenting. Here's a practical mapping for standard CRUD operations:
| Operation | Method | Success Code | Common Error Codes |
|---|---|---|---|
| List resources | GET | 200 | 401, 403 |
| Get single resource | GET | 200 | 401, 403, 404 |
| Create resource | POST | 201 | 400, 409, 422 |
| Full update | PUT | 200 | 400, 404, 409 |
| Partial update | PATCH | 200 | 400, 404, 422 |
| Delete resource | DELETE | 204 | 401, 403, 404 |
Common Mistakes
- Using 200 for everything — Even errors. This forces clients to parse the body to know if something went wrong. Use proper status codes.
- Returning 200 on creation — Use
201 Createdwith aLocationheader. It tells the client a new resource exists and where to find it. - Using 404 for empty collections — If
GET /api/users?role=adminreturns no results, that's a200with an empty array, not a404. The collection exists; it's just empty. - Confusing 401 and 403 — Remember:
401= "who are you?",403= "you can't do that." - Not using 409 for conflicts — Duplicate emails, stale updates, and race conditions deserve a
409, not a generic400.
Debugging with Status Codes
When something goes wrong, status codes are your first diagnostic clue. Here's how to inspect them efficiently.
Using curl
The -I flag sends a HEAD request and shows only response headers, including the status code:
# Show just headers
curl -I https://api.example.com/users
# Show headers + body with verbose output
curl -v https://api.example.com/users
# Follow redirects and show each hop
curl -v -L https://example.com/old-page
# POST with response headers
curl -i -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice"}'
Browser DevTools
Open the Network tab in your browser's developer tools (Cmd+Option+I on macOS, Ctrl+Shift+I on Windows/Linux). Every request shows its status code, response headers, and timing breakdown. You can filter by status code to quickly find errors:
- Click a request to see full request and response headers
- Filter by
status-code:4to show only 4xx errors - Check the Timing tab to see if slow responses are causing gateway timeouts
- Right-click a request and "Copy as cURL" to reproduce it in the terminal
Reading Response Headers
Status codes tell you what happened; response headers often tell you why:
- WWW-Authenticate on a
401— tells you what authentication scheme the server expects - Retry-After on a
429or503— tells you when to retry - Allow on a
405— lists the HTTP methods the endpoint supports - Location on a
201or3xx— tells you where the resource is - X-RateLimit-* headers — show your current rate limit status
Tip: When debugging 502 or 504 errors, the problem is almost never the proxy. Check the upstream application server's logs and response times first.
Quick Reference Table
| Code | Name | Meaning |
|---|---|---|
100 | Continue | Send the request body |
101 | Switching Protocols | Upgrading to WebSocket or HTTP/2 |
200 | OK | Request succeeded |
201 | Created | Resource created successfully |
204 | No Content | Success, no body returned |
206 | Partial Content | Range request fulfilled |
301 | Moved Permanently | Resource permanently moved (may change method) |
302 | Found | Temporary redirect (may change method) |
304 | Not Modified | Use cached version |
307 | Temporary Redirect | Temporary redirect (preserves method) |
308 | Permanent Redirect | Permanent redirect (preserves method) |
400 | Bad Request | Malformed request syntax |
401 | Unauthorized | Authentication required |
403 | Forbidden | Authenticated but not authorised |
404 | Not Found | Resource does not exist |
405 | Method Not Allowed | HTTP method not supported |
409 | Conflict | Conflicts with current resource state |
422 | Unprocessable Entity | Valid syntax but semantic errors |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Unhandled server-side failure |
502 | Bad Gateway | Invalid upstream response |
503 | Service Unavailable | Server temporarily down |
504 | Gateway Timeout | Upstream server timed out |
All HTTP Codes at Your Fingertips
BoltKit's HTTPCodes tool gives you a searchable, categorised reference for every HTTP status code — right on your iPhone or iPad. Instant lookup, no Googling required.
Get BoltKit Free