[{"data":1,"prerenderedAt":91},["ShallowReactive",2],{"qa-\u002Ffastapi\u002Fdeployment\u002Fmiddleware":3},{"page":4,"siblings":81,"blog":73},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"questionsCount":72,"related":73,"seo":74,"seoDescription":75,"stem":76,"subtopic":6,"topic":77,"topicSlug":78,"updated":79,"__hash__":80},"qa\u002Ffastapi\u002Fdeployment\u002Fmiddleware.md","Middleware",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","FastAPI","fastapi",{},true,"\u002Ffastapi\u002Fdeployment\u002Fmiddleware",[22,27,31,35,39,43,47,51,56,60,64,68],{"id":23,"difficulty":24,"q":25,"a":26},"middleware-basics","easy","What is middleware in FastAPI and how does it wrap requests?","Middleware is code that runs **around every request** — before the handler\n(on the way in) and after the handler (on the way out). It can modify the\nrequest, modify the response, short-circuit with an early response, or just\nobserve (logging, timing).\n\n```python\nfrom starlette.middleware.base import BaseHTTPMiddleware\nfrom fastapi import Request\n\nclass TimingMiddleware(BaseHTTPMiddleware):\n    async def dispatch(self, request: Request, call_next):\n        import time\n        start = time.perf_counter()\n        response = await call_next(request)   # call the rest of the stack\n        elapsed = time.perf_counter() - start\n        response.headers[\"X-Process-Time\"] = f\"{elapsed:.4f}\"\n        return response\n\napp.add_middleware(TimingMiddleware)\n```\n\nRule of thumb: middleware is for cross-cutting concerns (timing, CORS, auth\nheaders, compression); use dependencies for per-route business logic.\n",{"id":28,"difficulty":14,"q":29,"a":30},"middleware-order","What order does middleware execute in FastAPI?","Middleware added with `add_middleware()` wraps the app in **reverse addition\norder** — last added = outermost wrapper = runs first for requests.\n\n```python\napp.add_middleware(A)   # added first → innermost\napp.add_middleware(B)   # added second → middle\napp.add_middleware(C)   # added last  → outermost\n\n# Request:  C → B → A → handler\n# Response: handler → A → B → C\n```\n\nBuilt-in middleware (CORS, GZip, TrustedHost) should typically be outermost\n(added last) so they wrap everything including custom middleware.\n\nRule of thumb: add CORS middleware last (so it wraps everything) and logging\nmiddleware first (so its timing includes all other middleware).\n",{"id":32,"difficulty":14,"q":33,"a":34},"cors-middleware","How do you configure CORS middleware in FastAPI and what are the critical settings?","```python\nfrom fastapi.middleware.cors import CORSMiddleware\n\napp.add_middleware(\n    CORSMiddleware,\n    allow_origins=[\"https:\u002F\u002Fapp.example.com\", \"https:\u002F\u002Fadmin.example.com\"],\n    allow_credentials=True,     # required for cookies\u002Fauth headers cross-origin\n    allow_methods=[\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"],\n    allow_headers=[\"Content-Type\", \"Authorization\", \"X-Request-ID\"],\n    expose_headers=[\"X-Total-Count\"],   # headers the browser can read\n    max_age=3600,               # preflight cache duration (seconds)\n)\n```\n\nCritical interaction: `allow_origins=[\"*\"]` + `allow_credentials=True` is\n**invalid** — browsers reject this combination. You must list exact origins\nwhen using credentials.\n\nRule of thumb: list exact origins, never `\"*\"`, for APIs that send or receive\nauth cookies or `Authorization` headers.\n",{"id":36,"difficulty":24,"q":37,"a":38},"gzip-middleware","How do you enable GZip compression for FastAPI responses?","```python\nfrom starlette.middleware.gzip import GZipMiddleware\n\napp.add_middleware(GZipMiddleware, minimum_size=1000)\n```\n\n`minimum_size` (bytes) — responses smaller than this threshold are not\ncompressed (compression overhead isn't worth it for tiny responses).\n\nGZipMiddleware respects `Accept-Encoding: gzip` from the client — clients\nthat don't support gzip get uncompressed responses.\n\nRule of thumb: enable GZip for APIs that return large JSON payloads (lists of\nitems, reports); don't bother for APIs that only return small objects.\n",{"id":40,"difficulty":24,"q":41,"a":42},"trusted-host-middleware","What does `TrustedHostMiddleware` do and why is it important?","It validates the `Host` header against an allowlist, returning **400** if the\nhost doesn't match. This prevents **Host header injection attacks**:\n\n```python\nfrom starlette.middleware.trustedhost import TrustedHostMiddleware\n\napp.add_middleware(\n    TrustedHostMiddleware,\n    allowed_hosts=[\"api.example.com\", \"*.example.com\"],\n)\n```\n\nWithout it, an attacker who routes a request to your server with a forged `Host`\nheader can exploit password reset links, CORS checks, or server-generated URLs\nthat rely on `request.base_url`.\n\nRule of thumb: add `TrustedHostMiddleware` before deploying publicly; list\nall valid hostnames including your CDN\u002Fload balancer's hostname.\n",{"id":44,"difficulty":24,"q":45,"a":46},"https-redirect","How do you redirect all HTTP traffic to HTTPS in FastAPI?","```python\nfrom starlette.middleware.httpsredirect import HTTPSRedirectMiddleware\n\napp.add_middleware(HTTPSRedirectMiddleware)\n```\n\nAll HTTP requests receive a **307 Temporary Redirect** to the HTTPS equivalent.\nBehind a reverse proxy that terminates TLS, the proxy should handle the redirect\ninstead — the FastAPI middleware is a fallback.\n\nNote: this middleware checks the `X-Forwarded-Proto` header set by reverse proxies.\nConfigure Nginx\u002FALB to set this header; otherwise the middleware may redirect\nHTTPS requests (which arrive to Uvicorn as plain HTTP).\n\nRule of thumb: use `HTTPSRedirectMiddleware` as defence-in-depth; configure the\nprimary HTTP→HTTPS redirect at the reverse proxy level.\n",{"id":48,"difficulty":14,"q":49,"a":50},"base-http-middleware","What is `BaseHTTPMiddleware` and what is its performance trade-off?","`BaseHTTPMiddleware` provides a high-level `async dispatch(request, call_next)` \ninterface. It's easy to write but has a performance cost:\n- It buffers the response into memory before passing it to the next middleware.\n- It cannot stream responses incrementally.\n- It adds overhead compared to pure ASGI middleware.\n\n```python\nfrom starlette.middleware.base import BaseHTTPMiddleware\n\nclass RequestIDMiddleware(BaseHTTPMiddleware):\n    async def dispatch(self, request, call_next):\n        request.state.request_id = str(uuid4())\n        response = await call_next(request)\n        response.headers[\"X-Request-ID\"] = request.state.request_id\n        return response\n```\n\nFor high-performance production middleware, implement the raw ASGI interface:\n```python\nclass RawMiddleware:\n    def __init__(self, app):\n        self.app = app\n    async def __call__(self, scope, receive, send):\n        ...\n        await self.app(scope, receive, send)\n```\n\nRule of thumb: use `BaseHTTPMiddleware` for simplicity unless profiling shows\nit as a bottleneck; use raw ASGI middleware for streaming responses.\n",{"id":52,"difficulty":53,"q":54,"a":55},"middleware-exception-handling","hard","Can middleware catch exceptions raised by route handlers?","Yes — exceptions propagate through the middleware stack. Middleware can catch\nthem with `try\u002Fexcept` around `call_next()`:\n\n```python\nclass ErrorLoggingMiddleware(BaseHTTPMiddleware):\n    async def dispatch(self, request, call_next):\n        try:\n            response = await call_next(request)\n        except Exception as e:\n            logger.error(\"Unhandled error: %s\", e, exc_info=True)\n            return JSONResponse({\"detail\": \"Internal server error\"}, status_code=500)\n        return response\n```\n\nHowever, `HTTPException` is caught by FastAPI's exception handler **before**\npropagating through middleware — middleware doesn't see handled `HTTPException`\nresponses.\n\nRule of thumb: use middleware to catch **unexpected** exceptions (for logging);\nuse `@app.exception_handler` to convert **expected** domain exceptions to HTTP responses.\n",{"id":57,"difficulty":14,"q":58,"a":59},"request-id-middleware","How do you implement a request ID middleware for distributed tracing?","```python\nimport uuid\nfrom starlette.middleware.base import BaseHTTPMiddleware\n\nclass RequestIDMiddleware(BaseHTTPMiddleware):\n    async def dispatch(self, request, call_next):\n        # Use X-Request-ID if provided (from upstream), else generate one\n        req_id = request.headers.get(\"X-Request-ID\", str(uuid.uuid4()))\n        request.state.request_id = req_id\n        response = await call_next(request)\n        response.headers[\"X-Request-ID\"] = req_id\n        return response\n\napp.add_middleware(RequestIDMiddleware)\n```\n\nIn handlers and dependencies, access via `request.state.request_id` and include\nit in log records for correlation.\n\nRule of thumb: always propagate the request ID from upstream if present — it\nallows tracing a request across multiple services in a distributed system.\n",{"id":61,"difficulty":14,"q":62,"a":63},"session-middleware","How do you add server-side sessions to FastAPI?","Use `starlette.middleware.sessions.SessionMiddleware` (cookie-signed sessions):\n\n```python\nfrom starlette.middleware.sessions import SessionMiddleware\n\napp.add_middleware(\n    SessionMiddleware,\n    secret_key=settings.secret_key,\n    session_cookie=\"session\",\n    max_age=3600,     # 1 hour\n    https_only=True,  # cookie sent over HTTPS only\n    same_site=\"lax\",\n)\n```\n\nAccess in handlers:\n```python\n@app.get(\"\u002Fme\")\nasync def me(request: Request):\n    user_id = request.session.get(\"user_id\")\n    if not user_id:\n        raise HTTPException(401)\n    return {\"user_id\": user_id}\n\n@app.post(\"\u002Flogin\")\nasync def login(request: Request, ...):\n    request.session[\"user_id\"] = user.id\n    return {\"status\": \"logged in\"}\n```\n\nRule of thumb: Starlette sessions store data in a signed cookie — all session\ndata is sent to the browser. Use Redis-backed sessions for sensitive data.\n",{"id":65,"difficulty":53,"q":66,"a":67},"rate-limit-middleware","How do you implement rate limiting as middleware in FastAPI?","```python\nimport time\nfrom collections import defaultdict\nfrom fastapi import Request\nfrom fastapi.responses import JSONResponse\nfrom starlette.middleware.base import BaseHTTPMiddleware\n\nclass RateLimitMiddleware(BaseHTTPMiddleware):\n    def __init__(self, app, max_calls: int = 100, period: int = 60):\n        super().__init__(app)\n        self.max_calls = max_calls\n        self.period = period\n        self.calls: dict[str, list[float]] = defaultdict(list)\n\n    async def dispatch(self, request: Request, call_next):\n        key = request.client.host\n        now = time.time()\n        window_start = now - self.period\n\n        # Keep only calls within the window\n        self.calls[key] = [t for t in self.calls[key] if t > window_start]\n\n        if len(self.calls[key]) >= self.max_calls:\n            return JSONResponse({\"detail\": \"Rate limit exceeded\"}, status_code=429)\n\n        self.calls[key].append(now)\n        return await call_next(request)\n\napp.add_middleware(RateLimitMiddleware, max_calls=100, period=60)\n```\n\nFor production use Redis instead of in-memory dict (shared across workers).\n\nRule of thumb: the in-memory dict is per-worker — each worker has separate counters.\nUse Redis for a global rate limit that works across all workers.\n",{"id":69,"difficulty":14,"q":70,"a":71},"middleware-vs-dependency-auth","Should authentication be done in middleware or as a dependency?","| Approach | Pros | Cons |\n|----------|------|------|\n| Middleware | Runs before routing; can block early | Can't access route params; not testable via `dependency_overrides` |\n| Dependency | Testable; has route context; composable | Only runs after routing |\n\nBest practice — **hybrid**:\n- Middleware: lightweight token presence check (is `Authorization` header present?).\n- Dependency: full JWT validation + DB user lookup.\n\n```python\n# middleware — fast early reject\nclass BearerPresenceMiddleware(BaseHTTPMiddleware):\n    UNPROTECTED = {\"\u002Ftoken\", \"\u002Fdocs\", \"\u002Fhealth\"}\n    async def dispatch(self, request, call_next):\n        if request.url.path not in self.UNPROTECTED:\n            if not request.headers.get(\"Authorization\"):\n                return JSONResponse({\"detail\": \"Missing token\"}, status_code=401)\n        return await call_next(request)\n\n# dependency — full validation\nasync def get_current_user(token = Depends(oauth2_scheme)):\n    ...\n```\n\nRule of thumb: use middleware for coarse early rejection; use dependencies for\nfine-grained auth with route context.\n",12,null,{"description":11},"FastAPI middleware interview questions — BaseHTTPMiddleware, CORS, GZip, trusted hosts, custom middleware, execution order and performance.","fastapi\u002Fdeployment\u002Fmiddleware","Deployment & Middleware","deployment","2026-06-20","cUfNGerUgiltPnnDvZoyS0WmvV0LGhA6NzX308xPsgY",[82,86,87],{"subtopic":83,"path":84,"order":85},"Uvicorn & Gunicorn","\u002Ffastapi\u002Fdeployment\u002Fuvicorn-gunicorn",1,{"subtopic":6,"path":20,"order":12},{"subtopic":88,"path":89,"order":90},"Background Tasks","\u002Ffastapi\u002Fdeployment\u002Fbackground-tasks",3,1782244113861]