Skip to content

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:

  1. trust-boundary middleware (proxy/IP normalization)
  2. security middleware (auth/authorization)
  3. observability middleware (logging/tracing)
  4. 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.