Middleware¶
Middleware wraps an ASGI app to apply shared behavior.
Typical middleware responsibilities¶
- auth and access policies
- proxy header normalization
- request/response logging
- timing and tracing
- request IDs or correlation IDs
Proxy header middleware example:
from __future__ import annotations
from palfrey.middleware.proxy_headers import ProxyHeadersMiddleware
async def app(scope, receive, send):
"""Show the client tuple after trusted proxy processing."""
if scope["type"] != "http":
return
client = scope.get("client")
body = f"client={client}".encode()
await send(
{
"type": "http.response.start",
"status": 200,
"headers": [
(b"content-type", b"text/plain"),
(b"content-length", str(len(body)).encode("ascii")),
],
}
)
await send({"type": "http.response.body", "body": body})
wrapped_app = ProxyHeadersMiddleware(app, trusted_hosts="127.0.0.1")
Ordering matters¶
A practical order is:
- trust-boundary middleware (proxy/IP normalization)
- security middleware (auth/authorization)
- observability middleware (logging/tracing)
- application routing/handlers
Risks to avoid¶
- trusting forwarded headers from untrusted peers
- logging sensitive payloads by default
- putting expensive work in always-on middleware paths
Plain-language explanation¶
If the app is the main service desk, middleware are specialist desks that perform checks before and after each request.