Chapter 8 · Cookie and Header Parameters
Chapter goals: Learn to read Cookies and headers from requests using
CookieandHeader, understand the underscore auto-conversion rule, and master setting Cookies securely in responses.
8.1 Another Pair of "Sibling" Parameter Classes
Cookie and Header belong to the same family as Query and Path: they all inherit from the same Param base class, so they support identical default values, validation, and alias mechanisms.
Why explicit declaration is required
If you write only session_id: str = None, FastAPI interprets the parameter as a query parameter. To read from Cookies or headers, you must explicitly specify with Cookie() / Header() — these four classes are factory functions that return special parameter objects when imported from fastapi.
from typing import Optional
from fastapi import FastAPI, Cookie, Header
app = FastAPI()
@app.get("/items/")
async def read_items(
session_id: Optional[str] = Cookie(None), # Read Cookie named session_id
user_agent: Optional[str] = Header(None), # Read User-Agent header
x_token: Optional[str] = Header(None), # Read X-Token header
):
return {"session_id": session_id, "user_agent": user_agent, "x_token": x_token}Test:
curl http://127.0.0.1:8000/items/ \
-H "User-Agent: my-app/1.0" -H "X-Token: abc123" \
-b "session_id=xyz789"8.2 Underscore Auto-Conversion: convert_underscores
HTTP standard header naming uses hyphens (user-agent, content-type), but Python variable names cannot contain -. By default, Header auto-converts: mapping _ in the parameter name to -, and since HTTP headers are case-insensitive, user_agent matches User-Agent.
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/header-strict/")
async def read_strict_header(
# Some custom headers do use underscores (e.g. x_custom_key) — disable conversion then
x_custom_key: str = Header(..., convert_underscores=False),
):
return {"x_custom_key": x_custom_key}Think twice before disabling conversion
Some HTTP proxies and servers reject headers containing underscores (Nginx drops them by default). Confirm your entire chain allows them before setting convert_underscores=False.
Duplicate headers with the same name (e.g., multiple X-Token headers) can be received as a list with Optional[List[str]]; a single str receives only one value.
8.3 Setting Cookies in Responses
Reading relies on parameter declarations; writing uses response.set_cookie(). Add a Response type parameter to the path function (you do not need to return it manually):
from fastapi import FastAPI, Response
app = FastAPI()
@app.post("/login/")
async def login(response: Response, username: str):
response.set_cookie(
key="session_id",
value="s-123",
max_age=3600, # Validity in seconds
httponly=True, # Blocked from JS read — prevents XSS theft
secure=True, # Only sent over HTTPS
samesite="lax", # CSRF protection: cross-site requests do not carry it
path="/",
)
return {"msg": f"{username} logged in"}You can also call set_cookie on a JSONResponse returned directly. The three security flags are a common interview topic:
| Flag | Purpose |
|---|---|
httponly | Browser blocks JavaScript from reading via document.cookie, mitigating XSS session theft |
secure | Only sent over HTTPS connections |
samesite | Three levels: strict / lax / none; controls whether cross-site requests carry the cookie, mitigating CSRF |
Delete a Cookie with response.delete_cookie(key) — internally it sets a Set-Cookie header with an expiry in the past.
8.4 Typical Scenario: Session Tracking and Client Info
Combining this chapter's knowledge for a common middleware need — tracking client origin and maintaining a simple session:
import uuid
from fastapi import FastAPI, Header, Response
from typing import Optional
app = FastAPI()
@app.get("/track/")
async def track_client(
response: Response,
x_forwarded_for: Optional[str] = Header(None),
user_agent: Optional[str] = Header(None),
):
session_id = str(uuid.uuid4())
response.set_cookie(
key="session_id",
value=session_id,
httponly=True,
max_age=86400,
)
return {
"session_id": session_id,
"client_ip": x_forwarded_for or "unknown",
"browser": user_agent or "unknown",
}8.5 Chapter Summary
Cookie()andHeader()are sibling parameter classes toQueryandPath;convert_underscores=True(default) mapsuser_agent→User-Agent; setFalsefor custom headers with underscores;response.set_cookie()withhttponly,secure,samesitefor secure session management;response.delete_cookie()expires the cookie by setting a past expiry date.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. Why must you use Cookie() instead of just writing session_id: str = None?
2. What does the httponly flag prevent?
3. What is the effect of samesite="lax" on a cookie?
4. How does Header handle the name mismatch between Python variables (user_agent) and HTTP headers (User-Agent)?
🛠️ Hands-On Exercises
- Write a POST
/loginendpoint that acceptsusernameandpasswordas Form fields, sets a secure session Cookie, and returns a welcome message. - Write a GET
/profileendpoint that reads the session Cookie and theX-Client-Versionheader; return both values. - Test with curl: send a request with
-b "session_id=test123" -H "X-Client-Version: 2.0"and verify the response contains both values.
Proceed to Chapter 9: Error Handling and Custom Exceptions.