FastAPI Request Headers
HTTP headers provide crucial information about requests and responses in web applications. In FastAPI, working with request headers is straightforward yet powerful, giving you access to important metadata sent by clients. This guide will teach you how to access, validate, and utilize headers in your FastAPI applications.
What are HTTP Headers?
HTTP headers are key-value pairs sent with every HTTP request and response. They contain metadata about the request or response such as:
- Content type and length
- Authentication credentials
- Cache directives
- Client information (like user-agent)
- Custom application-specific data
Headers provide crucial context for API operations without being part of the main request body.
Accessing Headers in FastAPI
FastAPI provides several ways to access request headers:
Method 1: Using the Header
Parameter
The most common approach is using FastAPI's Header
class:
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/headers")
async def read_header(user_agent: str = Header(None)):
return {"User-Agent": user_agent}
In this example, FastAPI:
- Extracts the
User-Agent
header from the request - Converts it into the
user_agent
parameter - Returns it in the response
When you make a request to /headers
, you'll receive a JSON response with your browser's user-agent string:
{
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36..."
}
Method 2: Accessing the Request Object
You can also access all headers through the Request
object:
from fastapi import FastAPI, Request
app = FastAPI()
@app.get("/all-headers")
async def read_all_headers(request: Request):
return {"headers": dict(request.headers)}
This approach gives you access to all headers at once, returning something like:
{
"headers": {
"host": "localhost:8000",
"user-agent": "Mozilla/5.0...",
"accept": "text/html,application/xhtml+xml...",
"accept-encoding": "gzip, deflate",
"accept-language": "en-US,en;q=0.9",
"connection": "keep-alive"
}
}
Header Parameter Naming
HTTP headers are conventionally written with "dash-case" formatting (e.g., Content-Type
, User-Agent
). However, Python variables can't contain dashes. FastAPI handles this automatically:
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/header-conversion")
async def read_header(
user_agent: str = Header(None),
content_type: str = Header(None),
x_custom_header: str = Header(None)
):
return {
"User-Agent": user_agent,
"Content-Type": content_type,
"X-Custom-Header": x_custom_header
}
FastAPI automatically:
- Converts
user_agent
touser-agent
- Converts
content_type
tocontent-type
- Converts
x_custom_header
tox-custom-header
If you want to disable this automatic conversion, use the convert_underscores
parameter:
@app.get("/no-conversion")
async def no_conversion(
strange_header: str = Header(None, convert_underscores=False)
):
# Now FastAPI will look for a header named exactly "strange_header"
return {"strange_header": strange_header}
Required and Default Headers
Like other parameters, you can make headers required or provide default values:
from fastapi import FastAPI, Header
app = FastAPI()
# Required header
@app.get("/required-header")
async def required_header(api_key: str = Header(...)):
return {"api_key": api_key}
# Default value if header not provided
@app.get("/default-header")
async def default_header(accept: str = Header("application/json")):
return {"accept": accept}
For the /required-header
endpoint, if the api-key
header is not provided, FastAPI will return a validation error:
{
"detail": [
{
"loc": ["header", "api-key"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
Working with Multiple Header Values
Some HTTP headers can appear multiple times in a request. By default, FastAPI combines these into a single comma-separated string, but you can receive them as a list:
from fastapi import FastAPI, Header
from typing import List
app = FastAPI()
@app.get("/multiple-values")
async def multiple_values(
x_token: List[str] = Header(None)
):
return {"X-Token values": x_token}
If a client sends multiple X-Token
headers, FastAPI will provide them as a list.
Practical Examples
Example 1: API Key Authentication
A common use for headers is API authentication:
from fastapi import FastAPI, Header, HTTPException
app = FastAPI()
API_KEYS = {
"user1": "sk_test_abc123",
"user2": "sk_test_xyz789"
}
@app.get("/protected-resource")
async def protected_resource(api_key: str = Header(...)):
if api_key not in API_KEYS.values():
raise HTTPException(status_code=401, detail="Invalid API Key")
# Find the user associated with this key
for user, key in API_KEYS.items():
if key == api_key:
return {"message": f"Hello, {user}! Access granted."}
Example 2: Content Negotiation
Headers can determine what format a client prefers:
from fastapi import FastAPI, Header, HTTPException
from fastapi.responses import JSONResponse, PlainTextResponse, HTMLResponse
app = FastAPI()
data = {
"name": "FastAPI",
"description": "A modern, fast web framework for building APIs with Python"
}
@app.get("/content")
async def content_negotiation(accept: str = Header("application/json")):
if "application/json" in accept:
return JSONResponse(content=data)
elif "text/plain" in accept:
return PlainTextResponse(content=f"{data['name']}: {data['description']}")
elif "text/html" in accept:
html_content = f"""
<!DOCTYPE html>
<html>
<head><title>{data['name']}</title></head>
<body>
<h1>{data['name']}</h1>
<p>{data['description']}</p>
</body>
</html>
"""
return HTMLResponse(content=html_content)
else:
raise HTTPException(status_code=406, detail="Not Acceptable")
Example 3: Conditional Requests
Headers can help with resource caching:
import hashlib
from datetime import datetime
from fastapi import FastAPI, Header, Response
app = FastAPI()
content = "This is the resource content"
etag = hashlib.md5(content.encode()).hexdigest()
last_modified = datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT")
@app.get("/conditional")
async def conditional_request(
response: Response,
if_none_match: str = Header(None),
if_modified_since: str = Header(None)
):
# Add headers to the response
response.headers["ETag"] = etag
response.headers["Last-Modified"] = last_modified
# Check if the client has a fresh copy
if if_none_match == etag:
response.status_code = 304 # Not Modified
return ""
# Return the content
return {"content": content, "etag": etag, "last_modified": last_modified}
Common Headers and Their Uses
Here are some important HTTP headers you might work with:
Header | Description | Example |
---|---|---|
User-Agent | Client identification | Mozilla/5.0 (Windows NT 10.0; Win64; x64)... |
Authorization | Authentication credentials | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI... |
Content-Type | Media type of the body | application/json |
Accept | Acceptable content types | application/json, text/plain, */* |
Cookie | HTTP cookies | session=abc123; user=john |
X-* | Custom headers | X-Request-ID: 123456 |
Validation and Dependencies
You can apply the same validation rules to headers as you would with other parameters:
from fastapi import FastAPI, Header
from pydantic import BaseModel, validator
app = FastAPI()
@app.get("/validate-version")
async def validate_version(api_version: str = Header(...)):
# Custom validation
versions = ["1.0", "1.1", "2.0"]
if api_version not in versions:
return {"error": f"API version must be one of {versions}"}
return {"message": f"Using API version: {api_version}"}
Summary
Headers are a powerful tool in HTTP communication that allow you to send metadata with your requests and responses. In FastAPI:
- Use the
Header
class to extract and validate headers - FastAPI automatically converts between Python's snake_case and HTTP's dash-case
- You can make headers required or provide default values
- Headers can be used for authentication, content negotiation, caching, and more
Understanding how to work with headers gives you access to important client information and allows you to implement advanced API features like authentication, caching, and content negotiation.
Additional Resources
Exercises
- Create a FastAPI endpoint that returns the client's browser and operating system based on the User-Agent header.
- Implement a basic authentication system using the Authorization header.
- Build an endpoint that serves different content types (HTML, JSON, XML) based on the Accept header.
- Create a cache system using ETag and If-None-Match headers to avoid sending the same content twice.
- Design an API versioning system using custom headers.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)