FastAPI

FastAPI Cookies — Session Management & Client Storage

Thirdy Gayares
12 min read

🎓 What You Will Learn

  • Cookie Basics: Understanding HTTP cookies
  • Setting Cookies: Creating and storing cookies
  • Reading Cookies: Accessing cookie values from requests
  • Secure Cookies: HttpOnly, Secure, SameSite flags
  • Sessions: Using Starlette SessionMiddleware
  • Cookie Expiration: Managing cookie lifetime
CookiesSessionsSecurity

Cookies are small files stored on the client side. The server sends cookies in responses, the browser stores them, and sends them back with future requests. Perfect for sessions and preferences.

Cookie Lifecycle: Server sets cookie in response, browser stores it, browser includes it in all subsequent requests to that domain.

2Setting Cookies in FastAPI

Use Response.set_cookie() to set cookies in your responses.

app/main.py
from fastapi import FastAPI
from fastapi.responses import Response

app = FastAPI()

@app.get("/set-cookie")
async def set_cookie():
    response = Response({"message": "Cookie set"})
    response.set_cookie(key="user_id", value="12345")
    return response

@app.post("/login")
async def login(username: str):
    response = Response({"status": "logged in"})
    response.set_cookie(
        key="session_id",
        value="abc123def456",
        max_age=3600,  # 1 hour
        httponly=True,  # Not accessible via JavaScript
        secure=True,  # HTTPS only
        samesite="lax"  # CSRF protection
    )
    return response

3Reading Cookies from Requests

Access cookies from the request using the Cookie parameter.

app/main.py
from fastapi import FastAPI, Cookie
from typing import Optional

app = FastAPI()

@app.get("/get-cookie")
async def get_cookie(user_id: Optional[str] = Cookie(None)):
    return {"user_id": user_id}

@app.get("/profile")
async def get_profile(session_id: str = Cookie(...)):
    # session_id is required (... makes it mandatory)
    return {"session_id": session_id, "user": "John"}

4Secure Cookie Flags

Always use security flags when setting sensitive cookies.

FlagPurposeUse Case
HttpOnlyCookie not accessible via JavaScriptSession tokens, auth cookies
SecureCookie only sent over HTTPSProduction environments
SameSitePrevent CSRF attacksStrict=same-site only, Lax=navigation allowed
Max-AgeCookie expiration in secondsSessions, temporary tokens
DomainCookie accessible on specific domainsSub-domain sharing
app/main.py
@app.post("/login")
async def secure_login(username: str):
    response = Response({"status": "logged in"})
    response.set_cookie(
        key="session_token",
        value="encrypted_token_here",
        max_age=86400,  # 1 day
        httponly=True,  # Prevent JavaScript access
        secure=True,  # HTTPS only
        samesite="strict",  # Strict CSRF protection
        domain="example.com"  # Specific domain
    )
    return response

Never use HttpOnly=False for sensitive cookies. Always protect session and auth cookies with HttpOnly flag.

5Using Starlette SessionMiddleware

For production, use Starlette SessionMiddleware for automatic session management.

app/main.py
from fastapi import FastAPI, Request
from starlette.middleware.sessions import SessionMiddleware

app = FastAPI()

# Add session middleware
app.add_middleware(
    SessionMiddleware,
    secret_key="your-secret-key-here",
    max_age=3600,  # Session expires in 1 hour
    https_only=True,  # HTTPS only in production
    same_site="lax"
)

@app.post("/login")
async def login(request: Request, username: str):
    request.session["user"] = username
    return {"message": f"Logged in as {username}"}

@app.get("/profile")
async def profile(request: Request):
    user = request.session.get("user")
    if not user:
        return {"error": "Not logged in"}
    return {"user": user}

@app.post("/logout")
async def logout(request: Request):
    request.session.clear()
    return {"message": "Logged out"}

SessionMiddleware: Automatically encrypts session data and stores it in a signed cookie. No server-side storage needed.

Cookies can expire automatically based on max_age or expires.

app/main.py
from fastapi import FastAPI
from fastapi.responses import Response
from datetime import datetime, timedelta

app = FastAPI()

@app.post("/set-temporary-cookie")
async def set_temp_cookie():
    response = Response({"status": "temp cookie set"})
    # Expires in 30 minutes
    response.set_cookie(
        key="temp_token",
        value="temp_value",
        max_age=1800  # 30 minutes in seconds
    )
    return response

@app.post("/set-persistent-cookie")
async def set_persistent_cookie():
    response = Response({"status": "persistent cookie set"})
    # Expires at specific datetime
    expires = datetime.now() + timedelta(days=7)
    response.set_cookie(
        key="persistent",
        value="persistent_value",
        expires=expires
    )
    return response

@app.post("/delete-cookie")
async def delete_cookie():
    response = Response({"status": "cookie deleted"})
    # Delete by setting max_age to 0
    response.delete_cookie(key="session_id")
    return response

7Testing Cookies

Test cookie handling with TestClient.

tests/test_cookies.py
from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_set_cookie():
    response = client.get("/set-cookie")
    assert response.status_code == 200
    assert "user_id" in client.cookies

def test_cookie_roundtrip():
    # Set cookie
    client.get("/set-cookie")

    # Verify cookie is sent in next request
    response = client.get("/get-cookie")
    assert response.json()["user_id"] == "12345"

def test_session():
    # Login (sets session)
    client.post("/login?username=john")

    # Verify session is available
    response = client.get("/profile")
    assert response.json()["user"] == "john"

    # Logout
    client.post("/logout")

    # Verify session is cleared
    response = client.get("/profile")
    assert "error" in response.json()

8Cookie Security Best Practices

  • Always use httponly=True for session/auth cookies
  • Always use secure=True in production (HTTPS only)
  • Set appropriate samesite flag (Strict or Lax)
  • Use short max_age for sensitive cookies (1-24 hours)
  • Never store sensitive data directly in cookies (use sessions instead)
  • Validate and sanitize cookie values on the server
  • Use signed/encrypted cookies for integrity
  • Clear cookies on logout
  • Test cookie behavior across browsers
  • Monitor for suspicious cookie access patterns

9Cookies with CORS

When using CORS with cookies, special configuration is needed.

app/main.py
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
from starlette.middleware.sessions import SessionMiddleware

app = FastAPI()

# Session middleware must come BEFORE CORS
app.add_middleware(SessionMiddleware, secret_key="secret")

# CORS must allow credentials
app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://frontend.example.com"],
    allow_credentials=True,  # Important for cookies!
    allow_methods=["*"],
    allow_headers=["*"]
)

Middleware Order: SessionMiddleware must be added BEFORE CORSMiddleware. Middleware executes in reverse order (last added runs first).

10Common Cookie Patterns

PatternUse CaseExample
Session cookieTrack logged-in usersession_id with HttpOnly
Preference cookieRemember user preferencestheme, language, timezone
Tracking cookieAnalytics and behaviorGoogle Analytics _ga
CSRF token cookiePrevent CSRF attackscsrf_token matching form token
Auth cookieStateless authenticationJWT token in HttpOnly cookie

11Debugging Cookies

Use browser DevTools to inspect cookies.

bash
# Open browser DevTools (F12)
# Go to Application tab
# Click Cookies in left sidebar
# View all cookies for the domain

# Check cookie attributes:
# - Name: cookie name
# - Value: cookie value
# - Domain: which domain can access
# - Path: which paths can access
# - Expires/Max-Age: when it expires
# - HttpOnly: if JavaScript can access
# - Secure: if HTTPS only
# - SameSite: CSRF protection level

12Migrating from Cookies to Tokens

JWT tokens are an alternative to cookies for stateless authentication.

app/main.py
from fastapi import FastAPI, Depends
from fastapi.security import HTTPBearer, HTTPAuthCredentials

app = FastAPI()
security = HTTPBearer()

@app.post("/login")
async def login(username: str):
    token = create_jwt_token(username)
    response = Response({"token": token})
    # Store JWT in HttpOnly cookie
    response.set_cookie(
        key="access_token",
        value=token,
        httponly=True,
        secure=True,
        max_age=3600
    )
    return response

@app.get("/profile")
async def profile(token: str = Depends(get_token)):
    user = decode_jwt_token(token)
    return {"user": user}

13Summary & Recommendations

Master cookies to build secure, stateful FastAPI applications. Use SessionMiddleware for simplicity, or JWT tokens for stateless APIs.

🚀 Congratulations! You now understand how to securely handle cookies in FastAPI. Build session management and authentication with confidence!

About the Author

TG

Thirdy Gayares

Passionate developer creating custom solutions for everyone. I specialize in building user-friendly tools that solve real-world problems while maintaining the highest standards of security and privacy.