2.11. HTTP Status Codes
Table of Contents
Why HTTP Status Codes Matter
When a client sends an HTTP request to a server, the server always replies with an HTTP response that starts with a status line. Part of that status line is a status code, such as 200, 404, or 500.
The status code tells the client what happened:
- Did the request work?
- Was something wrong with what the client sent?
- Did something break on the server?
As a backend developer you must always pick the correct status code. It affects:
- How browsers behave, for example when to redirect.
- How API clients handle errors or retries.
- How monitoring and alerts detect problems.
An HTTP response must have exactly one status code.
Clients rely on accurate status codes to understand the result of a request.
Structure of an HTTP Status Code
A status code has:
- A 3 digit number, for example
200 - A reason phrase, for example
OK
Example status line:
HTTP/1.1 200 OKThe number is what really matters to machines. The reason phrase is mostly for humans and logging.
Status Code Classes
The first digit groups codes into classes:
| Class | Range | Meaning | Typical use example |
|---|---|---|---|
| 1xx | 100β199 | Informational, request still in progress | Rare in basic APIs |
| 2xx | 200β299 | Success | Request worked |
| 3xx | 300β399 | Redirection | Use another URL or location |
| 4xx | 400β499 | Client error | Client sent something invalid |
| 5xx | 500β599 | Server error | Server failed while handling a valid request |
Rule:
- If the client did something wrong, use 4xx.
- If the server failed or crashed, use 5xx.
- If all is fine, use 2xx.
Common 2xx Success Codes
200 OK
Used when a request is successfully processed and:
- There is a response body, and
- Nothing special like creation or deletion needs to be indicated.
Examples:
GET /users/123 HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json
{"id": 123, "name": "Alice"}PUT /users/123 HTTP/1.1
Content-Type: application/json
{"name": "New Name"}
HTTP/1.1 200 OK
Content-Type: application/json
{"id": 123, "name": "New Name"}
When building APIs, 200 OK is the "default success" for many operations.
201 Created
Use 201 Created when the server created a new resource as a result of the request, typically with POST.
Key points:
- A new resource was created.
- The response should usually include:
- A
Locationheader with the URL of the new resource. - Optionally, the representation of the created resource in the body.
Example:
POST /users HTTP/1.1
Content-Type: application/json
{"name": "Alice"}
HTTP/1.1 201 Created
Location: /users/123
Content-Type: application/json
{"id": 123, "name": "Alice"}204 No Content
Use 204 No Content when the request was successful but there is no response body.
Typical for:
- Successful
DELETE - Some
PUTorPATCHwhere you do not need to send updated data back
Example:
DELETE /users/123 HTTP/1.1
HTTP/1.1 204 No Content
If you send a body with 204, most clients will ignore it. Avoid sending a body with this code.
Common 3xx Redirection Codes
3xx responses tell the client that the resource is available somewhere else or has changed location.
Redirection responses usually include a Location header.
301 Moved Permanently
Resource has permanently moved to a new URL.
Use cases:
- URL structure changed, and you want browsers and search engines to use the new URL.
- For example, you moved
/old-pathto/new-path.
Example:
GET /old-path HTTP/1.1
HTTP/1.1 301 Moved Permanently
Location: /new-path
Browsers may cache this and always go to /new-path next time.
302 Found and 307 Temporary Redirect
Both indicate a temporary redirect, but they differ in method handling.
302 Found(historic) may change the HTTP method, for example fromPOSTtoGET.307 Temporary Redirectpreserves the HTTP method and body.
For modern APIs, prefer 307 when you want to keep the method.
Example with 307:
POST /upload HTTP/1.1
HTTP/1.1 307 Temporary Redirect
Location: /upload-server-2
The client should repeat the POST to /upload-server-2 with the same body.
304 Not Modified
Used with caching. It says:
You already have the latest version, you can reuse your cached copy.
There is no response body. The client uses its own cached body.
Example:
GET /image.png HTTP/1.1
If-None-Match: "abc123"
HTTP/1.1 304 Not Modified
You will learn more about caching later, but remember that 304 is part of HTTP caching.
Common 4xx Client Error Codes
4xx means the client did something wrong. The request is invalid in some way.
400 Bad Request
The server cannot understand the request. Often used when:
- JSON body is invalid or cannot be parsed.
- Request syntax is broken.
- Body is missing when it is required.
Example:
POST /users HTTP/1.1
Content-Type: application/json
{"name": "Alice" // missing closing brace
HTTP/1.1 400 Bad Request
Content-Type: application/json
{"error": "Invalid JSON body"}401 Unauthorized
Despite the name, 401 Unauthorized means "not authenticated".
Use 401 when:
- The request requires authentication and the user is not logged in, or
- The access token or credentials are missing or invalid.
Typical for protected APIs:
GET /profile HTTP/1.1
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
{"error": "Authentication required"}
Note the WWW-Authenticate header. It tells the client what kind of authentication is needed.
403 Forbidden
The client is authenticated but not allowed to perform this action.
Use 403 when:
- The user is logged in but does not have permission to access the resource.
Example:
DELETE /users/123 HTTP/1.1
Authorization: Bearer valid-but-normal-user-token
HTTP/1.1 403 Forbidden
Content-Type: application/json
{"error": "You do not have permission to delete this user"}Rule:
- Not logged in or invalid token β 401 Unauthorized
- Logged in but not allowed β 403 Forbidden
404 Not Found
The resource does not exist, or you do not want to reveal that it exists.
Typical when:
- A user requested a non existing URL.
- A record with a given ID does not exist.
Example:
GET /users/99999 HTTP/1.1
HTTP/1.1 404 Not Found
Content-Type: application/json
{"error": "User not found"}
Many APIs also use 404 for security reasons when the user tries to access someone else's data, even if the resource exists, to avoid information leaks.
405 Method Not Allowed
The URL exists, but the HTTP method is not allowed on it.
Example:
POST /users/123 HTTP/1.1
HTTP/1.1 405 Method Not Allowed
Allow: GET, PUT, DELETE
The Allow header lists valid methods for that endpoint.
This often happens by accident when you forget to implement a method route in your backend.
409 Conflict
There is a conflict with the current state of the resource.
Common cases:
- Trying to create a user with an email that already exists.
- Version conflicts in optimistic locking.
Example:
POST /users HTTP/1.1
Content-Type: application/json
{"email": "alice@example.com"}
HTTP/1.1 409 Conflict
Content-Type: application/json
{"error": "Email already exists"}422 Unprocessable Entity
The request is syntactically correct, but the data is semantically invalid.
Often used for:
- Validation errors, for example required fields missing or wrong formats.
- This is very common in modern JSON APIs.
Example:
POST /users HTTP/1.1
Content-Type: application/json
{"name": "", "email": "not-an-email"}
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"errors": [
{"field": "name", "message": "Name cannot be empty"},
{"field": "email", "message": "Invalid email address"}
]
}
Some frameworks use 400 for validation errors, others prefer 422. Be consistent inside one API.
Common 5xx Server Error Codes
5xx codes mean the server failed to handle a valid request. The problem is on the server side.
500 Internal Server Error
A general error for unexpected failures.
Use 500 when:
- An unhandled exception occurred.
- Something broke and you do not have a more specific error code.
Example:
GET /users HTTP/1.1
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{"error": "Something went wrong"}In production, you should log full details on the server, but send a simple message to the client.
502 Bad Gateway
This appears when there is a reverse proxy or gateway (like Nginx) in front of your app and the proxy gets an invalid response from the upstream server.
Example scenario:
- Client β Nginx β Your FastAPI app.
- Your app crashes or returns a malformed response.
- Nginx returns
502 Bad Gatewayto the client.
As a backend developer you often see 502 in logs when your application container is down.
503 Service Unavailable
The server is temporarily unavailable.
Use for:
- Maintenance windows.
- Overloaded server that cannot handle more requests.
Example:
GET /api HTTP/1.1
HTTP/1.1 503 Service Unavailable
Retry-After: 120
Content-Type: application/json
{"error": "Service temporarily unavailable"}
Retry-After tells the client how long to wait before retrying, in seconds or as a date.
504 Gateway Timeout
The gateway or proxy timed out waiting for a response from the upstream server.
Example scenario:
- Your app tries to call a slow database or external API and takes too long.
- The reverse proxy has a timeout, for example 30 seconds.
- It returns
504 Gateway Timeoutto the client.
Typical Status Codes for CRUD APIs
In backend development, you often build REST style APIs with CRUD operations:
- Create
- Read
- Update
- Delete
Here is a typical mapping:
| Operation | HTTP Method | Usual Status Code(s) |
|---|---|---|
| List resources | GET | 200 OK |
| Get single resource | GET | 200 OK, 404 Not Found |
| Create resource | POST | 201 Created, sometimes 400, 422 |
| Replace resource | PUT | 200 OK or 204 No Content, 404 |
| Partial update | PATCH | 200 OK or 204 No Content, 404 |
| Delete resource | DELETE | 204 No Content, 404 Not Found |
Example mini API:
- Create:
POST /tasks HTTP/1.1
Content-Type: application/json
{"title": "Write chapter"}
HTTP/1.1 201 Created
Location: /tasks/1
Content-Type: application/json
{"id": 1, "title": "Write chapter", "completed": false}- Get:
GET /tasks/1 HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json
{"id": 1, "title": "Write chapter", "completed": false}- Update:
PATCH /tasks/1 HTTP/1.1
Content-Type: application/json
{"completed": true}
HTTP/1.1 200 OK
Content-Type: application/json
{"id": 1, "title": "Write chapter", "completed": true}- Delete:
DELETE /tasks/1 HTTP/1.1
HTTP/1.1 204 No Content- Get deleted resource:
GET /tasks/1 HTTP/1.1
HTTP/1.1 404 Not Found
Content-Type: application/json
{"error": "Task not found"}Choosing the Right Status Code
A simple way to decide on a code:
- Did the request succeed?
- Yes β 2xx
- No β continue
- Should the client use another URL?
- Yes β 3xx
- Was the request itself invalid or not allowed?
- Yes β 4xx
- Did something unexpected go wrong on the server?
- Yes β 5xx
Some practical examples:
| Situation | Good code |
|---|---|
| JSON body missing required field | 400 or 422 |
User not logged in, trying to access /me | 401 |
| Logged in user without admin rights tries to delete another user | 403 or 404 |
| Resource with given ID does not exist | 404 |
| Duplicate email on registration | 409 |
| Unhandled exception in your code | 500 |
| Your database is down | 500 or 503 |
Rule:
Use the most specific code that matches what happened.
Avoid returning 200 OK for failures.
Summary
- HTTP status codes are 3 digit numbers that tell the client what happened.
- They are grouped into classes: informational, success, redirection, client error, server error.
- As a backend developer, you must:
- Return 2xx on success, especially
200,201,204. - Use 4xx when the client sent something wrong (
400,401,403,404,409,422). - Use 5xx when the server failed (
500,502,503,504). - Choosing correct codes makes your APIs easier to use, debug, and monitor.
You will use these codes constantly when you implement real backend endpoints in later chapters.
Views: 9
KAHIBARO