Decision records
ADR-0022: Trusted proxies (X-Forwarded-For, PROXY protocol)
- Status: Accepted (2026-10-06)
Context
Behind a load balancer every connection comes from the balancer: per-source limits
(ADR-0013) would throttle all clients together, access logs and REMOTE_ADDR would show
the balancer. Client-supplied X-Forwarded-For must never be believed blindly.
Decision
server.trusted_proxies: CIDRs of peers allowed to report the client address. Reloadable.- Header mode (any listener): for requests whose TCP peer is trusted, the client is the
right-most
X-Forwarded-Forentry that is not itself a trusted proxy (entries further left may be forged by the client). The chain forwarded upstream is the received chain plus the peer. From untrusted peers the header is ignored and replaced. - PROXY protocol (
listener.proxy_protocol: true, requirestrusted_proxies): v1 and v2 headers, read before TLS/HTTP withinheader_read_timeout, never past the header. Peers outsidetrusted_proxiesare closed immediately (untrusted_proxy); missing or malformed headers close the connection (proxy_protocol). v1UNKNOWN, v2LOCALand non-TCP families keep the peer address. PROXY listeners admit connections per client after reading the header (the accept loop never waits). - The effective client address feeds the per-source request limiter, the access log,
RequestContext.remote_addr(PHPREMOTE_ADDR, Node/Python viaX-Forwarded-For) and diagnostics. Connections from trusted peers are exempt from the per-source connection limit (a balancer multiplexes many clients); their requests are limited per client. Forwarded(RFC 7239) is not parsed.