HTTP response codes are used to indicate success, failure, and other properties about the result of an HTTP request. Regardless of the contents of an HTTP response message body, a client will act according to the response status code.
Table of Contents
- 1xx: Informational
- 2xx: Success
- 3xx: Redirection
- 4xx: Client error
- 5xx: Server error
- Additional status codes
- Takeaway
Each HTTP response is accompanied by a status code, and they are broken into five categories. Each of the response status codes is used to convey general information about the outcome of the request.
There are five primary categories of HTTP response codes, each identifiable by the first digit:
1xx: Informational
An informational response code informs the client that the request is continuing.
2xx: Success
A successful response was received, interpreted corrected, and has been accepted.
- 200 OK
- 201 Created
- 202 Accepted
- 203 Non-Authoritative Information
- 204 No Content
- 205 Reset Content
- 206 Partial Content
- 207 Multi-Status
- 208 Already Reported
- 218 This Is Fine
- 226 IM Used
3xx: Redirection
A redirection indicates that further action needs to take place before the request is completed.
- 300 Multiple Choices
- 301 Moved Permanently
- 302 Found
- 303 See Other
- 304 Not Modified
- 305 Use Proxy
- 306 Switch Proxy
- 307 Temporary Redirect
- 308 Permanent Redirect
4xx: Client error
A client error indicates that the request cannot be completed because of an issue with the client, or the syntax of the request.
- 400 Bad Request
- 401 Unauthorized
- 402 Payment Required
- 403 Forbidden
- 404 Not Found
- 405 Method Not Allowed
- 406 Not Acceptable
- 407 Proxy Authentication Required
- 408 Request Timeout
- 409 Conflict
- 410 Gone
- 411 Length Required
- 412 Precondition Failed
- 413 Payload Too Large
- 414 URI Too Long
- 415 Unsupported Media Type
- 416 Range Not Satisfiable
- 417 Expectation Failed
- 418 I’m a Teapot
- 419 Page Expired
- 420 Method Failure or Enhance Your Calm
- 421 Misdirected Request
- 422 Unprocessable Entity
- 423 Locked
- 424 Failed Dependency
- 425 Too Early
- 426 Upgrade Required
- 428 Precondition Required
- 429 Too Many Requests
- 430 HTTP Status Code
- 431 Request Header Fields Too Large
- 440 Login Time-Out
- 444 No Response
- 449 Retry With
- 450 Blocked by Windows Parental Controls
- 451 Unavailable For Legal Reasons
- 460 Client Closed Connection Prematurely
- 463 Too Many Forwarded IP Addresses
- 464 Incompatible Protocol
- 494 Request Header Too Large
- 495 SSL Certificate Error
- 496 SSL Certificate Required
- 497 HTTP Request Sent to HTTPS Port
- 498 Invalid Token
- 499 Token Required or Client Closed Request
5xx: Server error
A server error indicates that the request is valid but cannot be completed because of an issue on the server’s side, such as a lack of available resources.
- 500 Internal Server Error
- 501 Not Implemented
- 502 Bad Gateway
- 503 Service Unavailable
- 504 Gateway Timeout
- 505 HTTP Version Not Supported
- 506 Variant Also Negotiates
- 507 Insufficient Storage
- 508 Loop Detected
- 509 Bandwidth Limit Exceeded
- 510 Not Extended
- 511 Network Authentication Required
- 520 Web Server Is Returning an Unknown Error
- 521 Web Server Is Down
- 522 Connection Timed Out
- 523 Origin Is Unreachable
- 524 A Timeout Occurred
- 525 SSL Handshake Failed
- 526 Invalid SSL Certificate
- 527 Railgun Listener to Origin
- 529 The Service Is Overloaded
- 530 Site Frozen
- 561 Unauthorized
- 598 Network Read Timeout Error
- 599 Network Connect Timeout Error
Additional status codes
In addition to the five primary categories of HTTP status codes mentioned above, the following status codes can also be encountered on the World Wide Web.
- 110 Response Is Stale
- 111 Revalidation Failed
- 112 Disconnected Operation
- 113 Heuristic Expiration
- 199 Miscellaneous Warning
- 214 Transformation Applied
- 299 Miscellaneous Persistent Warning
- 999 Unauthorized
Takeaway
HTTP responses are always accompanied by an HTTP response status code. The first digit of a status code indicates the category, which often indicates whether the request succeeded or failed. Each status code can be a valuable clue when troubleshooting problems between a client and server.
I’ll create a comprehensive FastAPI example demonstrating HTTP status codes with Mermaid diagrams. This will be quite extensive, so I’ll organize it into sections.
from fastapi import FastAPI, HTTPException, status, Response, Request, Depends
from fastapi.responses import JSONResponse, RedirectResponse
from pydantic import BaseModel
import time
import random
from typing import Optional
app = FastAPI(title="HTTP Status Codes Demo", version="1.0.0")
# Models
class Item(BaseModel):
id: int
name: str
description: Optional[str] = None
class User(BaseModel):
id: int
username: str
email: str
# In-memory storage
items_db = {}
users_db = {}
# ===== 1xx: Informational =====
@app.post("/upload-large-file", status_code=status.HTTP_100_CONTINUE)
async def upload_large_file(request: Request):
"""Example of 100 Continue - used for large file uploads"""
# Simulate checking if we can accept the upload
content_length = request.headers.get("content-length")
if content_length and int(content_length) > 1000000: # 1MB limit
return {"message": "Continue with upload", "status": "100 Continue"}
return {"message": "File size acceptable"}
@app.websocket("/ws")
async def websocket_endpoint():
"""Example of 101 Switching Protocols - WebSocket upgrade"""
# This would normally handle the protocol switch
pass
# ===== 2xx: Success =====
@app.get("/items", status_code=status.HTTP_200_OK)
async def get_items():
"""200 OK - Standard successful response"""
return {"items": list(items_db.values()), "count": len(items_db)}
@app.post("/items", status_code=status.HTTP_201_CREATED)
async def create_item(item: Item):
"""201 Created - Resource successfully created"""
items_db[item.id] = item
return {"message": "Item created successfully", "item": item}
@app.post("/items/{item_id}/process", status_code=status.HTTP_202_ACCEPTED)
async def process_item(item_id: int):
"""202 Accepted - Request accepted for processing"""
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
return {"message": "Item processing started", "item_id": item_id, "status": "processing"}
@app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_item(item_id: int):
"""204 No Content - Successful deletion with no content to return"""
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
del items_db[item_id]
return Response(status_code=status.HTTP_204_NO_CONTENT)
@app.put("/items/{item_id}/reset", status_code=status.HTTP_205_RESET_CONTENT)
async def reset_item_form(item_id: int):
"""205 Reset Content - Tells client to reset form"""
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
return {"message": "Form should be reset", "item_id": item_id}
@app.get("/items/{item_id}/download")
async def download_partial_item(item_id: int, range_header: Optional[str] = None):
"""206 Partial Content - Partial resource delivery"""
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
# Simulate partial content delivery
if range_header:
return Response(
content="Partial content data...",
status_code=status.HTTP_206_PARTIAL_CONTENT,
headers={"Content-Range": "bytes 0-100/1000"}
)
return items_db[item_id]
# ===== 3xx: Redirection =====
@app.get("/old-items")
async def old_items_endpoint():
"""301 Moved Permanently - Resource has permanently moved"""
return RedirectResponse(
url="/items",
status_code=status.HTTP_301_MOVED_PERMANENTLY
)
@app.get("/temp-items")
async def temp_items_endpoint():
"""302 Found - Temporary redirect"""
return RedirectResponse(
url="/items",
status_code=status.HTTP_302_FOUND
)
@app.post("/submit-form")
async def submit_form():
"""303 See Other - Redirect after POST"""
# Process form data
return RedirectResponse(
url="/items",
status_code=status.HTTP_303_SEE_OTHER
)
@app.get("/items/{item_id}/cached")
async def get_cached_item(item_id: int, if_modified_since: Optional[str] = None):
"""304 Not Modified - Resource hasn't changed"""
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
# Simulate checking if modified
if if_modified_since:
return Response(status_code=status.HTTP_304_NOT_MODIFIED)
return items_db[item_id]
@app.get("/permanent-redirect")
async def permanent_redirect():
"""308 Permanent Redirect - Permanent redirect preserving method"""
return RedirectResponse(
url="/items",
status_code=status.HTTP_308_PERMANENT_REDIRECT
)
# ===== 4xx: Client Error =====
@app.post("/bad-request-example")
async def bad_request_example(data: dict):
"""400 Bad Request - Malformed request"""
if not data or "required_field" not in data:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Missing required_field in request body"
)
return {"message": "Request processed successfully"}
@app.get("/protected-resource")
async def protected_resource(authorization: Optional[str] = None):
"""401 Unauthorized - Authentication required"""
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Authentication credentials required",
headers={"WWW-Authenticate": "Bearer"}
)
return {"message": "Access granted to protected resource"}
@app.get("/premium-content")
async def premium_content():
"""402 Payment Required - Payment needed"""
raise HTTPException(
status_code=status.HTTP_402_PAYMENT_REQUIRED,
detail="Payment required to access premium content"
)
@app.get("/admin-only")
async def admin_only(user_role: Optional[str] = None):
"""403 Forbidden - Access denied"""
if user_role != "admin":
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Access forbidden: Admin role required"
)
return {"message": "Admin panel access granted"}
@app.get("/items/{item_id}")
async def get_item(item_id: int):
"""404 Not Found - Resource doesn't exist"""
if item_id not in items_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Item with id {item_id} not found"
)
return items_db[item_id]
@app.post("/items/{item_id}")
async def method_not_allowed(item_id: int):
"""405 Method Not Allowed - HTTP method not supported"""
raise HTTPException(
status_code=status.HTTP_405_METHOD_NOT_ALLOWED,
detail="POST method not allowed for this endpoint",
headers={"Allow": "GET, PUT, DELETE"}
)
@app.get("/items-xml")
async def get_items_xml(accept: Optional[str] = None):
"""406 Not Acceptable - Cannot produce acceptable response"""
if accept and "application/xml" in accept:
raise HTTPException(
status_code=status.HTTP_406_NOT_ACCEPTABLE,
detail="XML format not supported, only JSON available"
)
return {"items": list(items_db.values())}
@app.post("/slow-endpoint")
async def slow_endpoint():
"""408 Request Timeout - Request took too long"""
# Simulate slow processing
await asyncio.sleep(10) # This would timeout in practice
raise HTTPException(
status_code=status.HTTP_408_REQUEST_TIMEOUT,
detail="Request processing timeout"
)
@app.put("/items/{item_id}/conflict")
async def update_with_conflict(item_id: int, item: Item):
"""409 Conflict - Request conflicts with current state"""
if item_id in items_db and items_db[item_id].name == item.name:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Item with this name already exists"
)
items_db[item_id] = item
return {"message": "Item updated successfully"}
@app.get("/deprecated-endpoint")
async def deprecated_endpoint():
"""410 Gone - Resource no longer available"""
raise HTTPException(
status_code=status.HTTP_410_GONE,
detail="This endpoint has been permanently removed"
)
@app.post("/require-content-length")
async def require_content_length(request: Request):
"""411 Length Required - Content-Length header required"""
if "content-length" not in request.headers:
raise HTTPException(
status_code=status.HTTP_411_LENGTH_REQUIRED,
detail="Content-Length header is required"
)
return {"message": "Request processed successfully"}
@app.post("/large-payload")
async def large_payload(request: Request):
"""413 Payload Too Large - Request body too large"""
content_length = request.headers.get("content-length")
if content_length and int(content_length) > 1000: # 1KB limit
raise HTTPException(
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
detail="Payload too large, maximum 1KB allowed"
)
return {"message": "Payload size acceptable"}
@app.get("/teapot")
async def teapot():
"""418 I'm a Teapot - Easter egg status code"""
raise HTTPException(
status_code=status.HTTP_418_IM_A_TEAPOT,
detail="I'm a teapot, I cannot brew coffee!"
)
@app.post("/rate-limited")
async def rate_limited():
"""429 Too Many Requests - Rate limit exceeded"""
# Simulate rate limiting
if random.choice([True, False]):
raise HTTPException(
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
detail="Rate limit exceeded",
headers={"Retry-After": "60"}
)
return {"message": "Request processed successfully"}
# ===== 5xx: Server Error =====
@app.get("/server-error")
async def server_error():
"""500 Internal Server Error - Generic server error"""
# Simulate server error
if random.choice([True, False]):
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="An unexpected server error occurred"
)
return {"message": "Request processed successfully"}
@app.get("/not-implemented")
async def not_implemented():
"""501 Not Implemented - Feature not implemented"""
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED,
detail="This feature is not yet implemented"
)
@app.get("/bad-gateway")
async def bad_gateway():
"""502 Bad Gateway - Invalid response from upstream server"""
# Simulate gateway error
raise HTTPException(
status_code=status.HTTP_502_BAD_GATEWAY,
detail="Invalid response received from upstream server"
)
@app.get("/service-unavailable")
async def service_unavailable():
"""503 Service Unavailable - Service temporarily unavailable"""
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Service temporarily unavailable",
headers={"Retry-After": "120"}
)
@app.get("/gateway-timeout")
async def gateway_timeout():
"""504 Gateway Timeout - Upstream server timeout"""
raise HTTPException(
status_code=status.HTTP_504_GATEWAY_TIMEOUT,
detail="Upstream server timeout"
)
# Health check endpoint
@app.get("/health")
async def health_check():
return {"status": "healthy", "timestamp": time.time()}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)PythonNow, let me create Mermaid diagrams to visualize the HTTP status code flow:
HTTP Status Codes Flow Diagrams
Overall HTTP Status Code Categories
graph TD
A[HTTP Request] --> B{Status Code Category}
B -->|1xx| C[Informational Response]
B -->|2xx| D[Success Response]
B -->|3xx| E[Redirection Response]
B -->|4xx| F[Client Error Response]
B -->|5xx| G[Server Error Response]
C --> C1[100 Continue]
C --> C2[101 Switching Protocols]
C --> C3[102 Processing]
C --> C4[103 Early Hints]
D --> D1[200 OK]
D --> D2[201 Created]
D --> D3[202 Accepted]
D --> D4[204 No Content]
D --> D5[206 Partial Content]
E --> E1[301 Moved Permanently]
E --> E2[302 Found]
E --> E3[303 See Other]
E --> E4[304 Not Modified]
E --> E5[308 Permanent Redirect]
F --> F1[400 Bad Request]
F --> F2[401 Unauthorized]
F --> F3[403 Forbidden]
F --> F4[404 Not Found]
F --> F5[429 Too Many Requests]
G --> G1[500 Internal Server Error]
G --> G2[502 Bad Gateway]
G --> G3[503 Service Unavailable]
G --> G4[504 Gateway Timeout]Authentication and Authorization Flow
sequenceDiagram
participant Client
participant Server
participant AuthService
Client->>Server: GET /protected-resource
Server->>Server: Check Authorization Header
alt No Authorization Header
Server->>Client: 401 Unauthorized
Note over Client: WWW-Authenticate: Bearer
else Invalid Token
Server->>AuthService: Validate Token
AuthService->>Server: Invalid
Server->>Client: 401 Unauthorized
else Valid Token but Insufficient Permissions
Server->>AuthService: Check Permissions
AuthService->>Server: Insufficient Rights
Server->>Client: 403 Forbidden
else Valid Token and Permissions
Server->>Client: 200 OK + Resource Data
endRedirection Flow
graph LR
A[Client Request] --> B{Resource Location}
B -->|Permanently Moved| C[301 Moved Permanently]
B -->|Temporarily Moved| D[302 Found]
B -->|Form Submission Result| E[303 See Other]
B -->|Not Modified| F[304 Not Modified]
B -->|Permanent with Method Preservation| G[308 Permanent Redirect]
C --> H[Client Follows Redirect]
D --> H
E --> H
G --> H
F --> I[Client Uses Cached Version]
H --> J[New Request to Redirect Location]
J --> K[Final Response]Error Handling Flow
flowchart TD
A[HTTP Request] --> B{Request Validation}
B -->|Valid| C{Authentication}
B -->|Invalid| D[400 Bad Request]
C -->|Authenticated| E{Authorization}
C -->|Not Authenticated| F[401 Unauthorized]
E -->|Authorized| G{Resource Exists}
E -->|Not Authorized| H[403 Forbidden]
G -->|Exists| I{Rate Limiting}
G -->|Not Found| J[404 Not Found]
I -->|Under Limit| K{Server Processing}
I -->|Over Limit| L[429 Too Many Requests]
K -->|Success| M[2xx Success]
K -->|Server Error| N[5xx Server Error]
style D fill:#E3B9BC
style F fill:#E3B9BC
style H fill:#E3B9BC
style J fill:#E3B9BC
style L fill:#E3B9BC
style N fill:#E3B9BC
style M fill:#CDE7B0API Request Lifecycle
stateDiagram-v2
[*] --> RequestReceived
RequestReceived --> ValidatingRequest
ValidatingRequest --> BadRequest : Invalid syntax
ValidatingRequest --> Authenticating : Valid syntax
Authenticating --> Unauthorized : No/Invalid credentials
Authenticating --> Authorizing : Valid credentials
Authorizing --> Forbidden : Insufficient permissions
Authorizing --> ProcessingRequest : Authorized
ProcessingRequest --> NotFound : Resource doesn't exist
ProcessingRequest --> Conflict : Resource conflict
ProcessingRequest --> ServerError : Internal error
ProcessingRequest --> Success : Successful processing
BadRequest --> [*] : 400
Unauthorized --> [*] : 401
Forbidden --> [*] : 403
NotFound --> [*] : 404
Conflict --> [*] : 409
ServerError --> [*] : 500
Success --> [*] : 2xxCache and Content Negotiation
sequenceDiagram
participant Client
participant Cache
participant Server
Client->>Cache: GET /resource
Cache->>Cache: Check if cached and fresh
alt Cache Hit and Fresh
Cache->>Client: 200 OK + Cached Data
else Cache Hit but Stale
Cache->>Server: GET /resource (If-Modified-Since)
alt Resource Modified
Server->>Cache: 200 OK + New Data
Cache->>Client: 200 OK + New Data
else Resource Not Modified
Server->>Cache: 304 Not Modified
Cache->>Client: 200 OK + Cached Data
end
else Cache Miss
Cache->>Server: GET /resource
Server->>Cache: 200 OK + Data
Cache->>Client: 200 OK + Data
endThis comprehensive example demonstrates:
- FastAPI implementation of various HTTP status codes across all categories
- Practical use cases for each status code
- Proper error handling and response formatting
- Mermaid diagrams showing:
- Overall status code categorization
- Authentication/authorization flows
- Redirection patterns
- Error handling workflows
- API request lifecycle
- Caching and content negotiation
To run this example:
pip install fastapi uvicorn
python fastapi_status_codes_example.pyBashThen visit http://localhost:8000/docs to see the interactive API documentation with all the endpoints demonstrating different HTTP status codes.
Discover more from Altgr Blog
Subscribe to get the latest posts sent to your email.
