Headers and Cookies

HTTP headers carry metadata (auth tokens, content negotiation, tracing IDs) and cookies carry small client-side state. FastAPI reads them with Header and Cookie, and writes them through the Response object.

Reading Headers

from fastapi import FastAPI, Header
from typing import Optional

app = FastAPI()

@app.get("/items/")
def read_items(
    user_agent: Optional[str] = Header(default=None),
    x_token: Optional[str] = Header(default=None),
):
    return {"User-Agent": user_agent, "X-Token": x_token}

Underscores convert to hyphens automatically: the parameter user_agent reads the User-Agent header. To keep underscores literal, use Header(convert_underscores=False).

Setting Response Headers

from fastapi import FastAPI, Response

app = FastAPI()

@app.get("/download/")
def download(response: Response):
    response.headers["X-Process-Time"] = "0.42"
    response.headers["Content-Disposition"] = 'attachment; filename="report.csv"'
    return {"status": "ok"}

Or return a JSONResponse directly when you need full control (note: then the return value of your function is ignored and the response is built manually).

Reading and Setting Cookies

from fastapi import FastAPI, Cookie, Response

app = FastAPI()

@app.get("/preferences/")
def get_preferences(session_id: Optional[str] = Cookie(default=None)):
    return {"session_id": session_id}

@app.post("/set-cookie/")
def set_cookie(response: Response):
    response.set_cookie(
        key="session_id",
        value="abc123",
        httponly=True,      # JavaScript cannot read it (XSS protection)
        secure=True,        # only sent over HTTPS
        samesite="lax",     # CSRF mitigation
        max_age=3600,
    )
    return {"message": "cookie set"}

@app.delete("/unset-cookie/")
def unset_cookie(response: Response):
    response.delete_cookie("session_id")
    return {"message": "cookie removed"}

Headers You Will Actually Use

Quick Questions and Answers

Q: Header names are case-insensitive in HTTP — does FastAPI care?

A: No. Header lookup is case-insensitive; declaring x_token matches X-Token, x-token, etc. The underscore-to-hyphen conversion is the only transformation applied.

Q: Why is my Set-Cookie header not reaching the browser?

A: Common causes: calling set_cookie on a locally-created JSONResponse instead of the injected Response; CORS — the frontend must use credentials: "include" and the server must set Access-Control-Allow-Credentials: true with an exact origin (wildcard * blocks cookies); or secure=True while testing over plain HTTP.

Q: Headers vs cookies vs local storage for auth tokens?

A: Authorization headers are the standard for APIs (stateless, no CSRF cookie issues). HttpOnly cookies protect against XSS but need CSRF protection. localStorage is readable by any injected script — generally avoid for tokens.

Conclusion

Declare Header/Cookie parameters to read metadata, and use the injected Response to set headers and cookies with secure flags. Authentication and tracing dependencies (coming in Intermediate) build exactly on these primitives.