Skip to content

Trusted proxies

Forward-auth headers like Remote-User are only meaningful when they are set by a reverse proxy you trust. Any client that can reach your app directly could set those headers itself and impersonate anyone. That is why the trusted-proxy allowlist is mandatory in this library, and why Tinyauth's own docs warn:

Ensure a list of trusted proxy URLs is configured for the app. Accepting headers from untrusted proxies can lead to security vulnerabilities.

How the check works

TinyauthMiddleware reads request.client.host — the immediate TCP peer that opened the connection — and checks it against the networks parsed from TinyauthConfig.trusted_proxies. If the peer is not in any trusted network, the request is rejected with HTTP 403 and the headers are ignored.

config = TinyauthConfig(trusted_proxies=["10.0.0.0/8", "127.0.0.1/32", "::1/128"])

Entries may be CIDR ranges (10.0.0.0/8) or bare addresses (10.0.0.5, treated as a single host). IPv4 and IPv6 are both supported.

Important caveat: what the peer actually is

request.client.host is the address of whatever opened the TCP connection to your app. In a typical deployment that is your reverse proxy, not the real end user:

end user --> Tinyauth / reverse proxy --> your FastAPI app
                    (the peer your app sees)

So trusted_proxies should contain the address(es) of your proxy, not of end users. This is exactly what you want: it ensures the identity headers were attached by your proxy.

What this check does not do:

  • It does not parse X-Forwarded-For, so it cannot restrict which end users (behind the proxy) may reach the app. Do that with access controls on the proxy itself (Tinyauth's group rules, or firewall/ACLs).
  • It does not defend against a compromised or misconfigured proxy that forwards attacker-controlled headers.

Choosing values

  • Docker / compose network: trust the proxy service's subnet, for example 172.16.0.0/12 or the specific bridge network CIDR.
  • Same host (loopback): 127.0.0.1/32 and ::1/128.
  • Kubernetes: trust the pod/service CIDR that the ingress/auth proxy uses.

Prefer the narrowest range that still covers your proxy.

Testing note

Starlette's TestClient reports its peer host as the string "testclient", which is not a valid IP and is therefore untrusted — every request would get a 403. Construct the client with an explicit trusted peer:

from starlette.testclient import TestClient

client = TestClient(app, client=("127.0.0.1", 12345))