Headers, Cookies & the Request Object
IntermediateHeader() and Cookie() bind request metadata as typed parameters (X-API-Key becomes x_api_key); Response.set_cookie sends session cookies with the security flags that interviews love; Request is the escape hatch.
Overview
Beyond the body and URL, requests carry metadata: headers (auth tokens, API keys, trace IDs, client info) and cookies (sessions, preferences). FastAPI binds both as typed parameters — Header() auto-converts underscores to hyphens, so x_request_id reads X-Request-ID — keeping them validated and documented like everything else. On the way out, response.set_cookie writes cookies, and the trio of flags (httponly, secure, samesite) is the difference between a session cookie and an XSS-stealable one. When you need the raw request — client IP behind a proxy, the exact URL, an unusual body — the Request object is the escape hatch, and X-Forwarded-For handling is its classic gotcha. This chapter is also the quiet foundation for the Auth category later.
Reading Headers and Cookies as Parameters
Header() and Cookie() are Query()'s siblings for metadata. Python names map to header names (x_api_key → X-Api-Key). Defaults make them optional; missing required ones produce the standard 422.
from fastapi import FastAPI, Header, Cookie, HTTPException
app = FastAPI()
@app.get("/profile")
def profile(
x_api_key: str = Header(), # required: X-Api-Key
user_agent: str | None = Header(default=None), # standard header, optional
x_request_id: str | None = Header(default=None), # tracing id from gateway
session_id: str | None = Cookie(default=None), # cookie by name
):
if x_api_key != "tophub-secret-1": # (real auth → Auth chapters)
raise HTTPException(401, "invalid API key")
return {
"client": user_agent,
"trace": x_request_id,
"has_session": session_id is not None,
}
# curl http://127.0.0.1:8000/profile \
# -H "X-Api-Key: tophub-secret-1" \
# -H "X-Request-ID: req-7f3a" \
# --cookie "session_id=abc123"
# Notes:
# - underscore ↔ hyphen conversion is automatic (convert_underscores=True)
# - headers are case-insensitive per HTTP spec
# - Authorization: Bearer <token> is usually read via Security utilities
# (OAuth2PasswordBearer) rather than raw Header() — coming in AuthSetting Cookies + the Raw Request
response.set_cookie sends Set-Cookie with security flags: httponly blocks JavaScript access (XSS), secure restricts to HTTPS, samesite curbs CSRF. Request exposes url, method, headers, and client — with the proxy caveat on IPs.
from fastapi import FastAPI, Request, Response
app = FastAPI()
@app.post("/login")
def login(response: Response): # declare Response to decorate it
response.set_cookie(
key="session_id",
value="sess-91f2c7", # (signed/random in real life)
max_age=60 * 60 * 12, # 12 hours
httponly=True, # JS cannot read it → XSS cannot steal the session
secure=True, # HTTPS only → no leaking on plain HTTP
samesite="lax", # not sent on cross-site POSTs → CSRF protection
)
return {"status": "logged in"}
@app.post("/logout")
def logout(response: Response):
response.delete_cookie("session_id")
return {"status": "logged out"}
@app.get("/whoami")
def whoami(request: Request): # the escape hatch
# Behind nginx/ALB, request.client.host is the PROXY's IP.
# The real client rides in X-Forwarded-For: "client, proxy1, proxy2"
fwd = request.headers.get("x-forwarded-for")
real_ip = fwd.split(",")[0].strip() if fwd else request.client.host
return {
"ip": real_ip,
"method": request.method,
"path": str(request.url.path),
}
# Caveat: X-Forwarded-For is spoofable unless a trusted proxy strips/sets it —
# only trust it when YOUR infrastructure is the one writing it.Key Points to Remember
- 1Header()/Cookie() bind metadata as typed params; x_api_key ↔ X-Api-Key mapping is automatic
- 2set_cookie with httponly + secure + samesite="lax" is the baseline session recipe
- 3Request is the escape hatch: url, method, headers, client
- 4Behind a proxy, the client IP is in X-Forwarded-For — and only trustworthy from your own proxy
Interview Questions
Sign in to ask AriaWhat do httponly, secure, and samesite each protect a session cookie from?
Your FastAPI service is behind nginx — why does request.client.host show one IP for everyone, and what is the fix?
Design request tracing: where does X-Request-ID come from and how does it flow through services?
Ask Aria about Headers, Cookies & the Request Object
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.