Skip to content

How TPS determines the client IP

Jeremy Echols edited this page Aug 20, 2026 · 1 revision

TPS cares about the client's IP address in three places: token IP tracking, bypass keys, and the analytics event log. Since TPS always sits behind a front proxy (Caddy, nginx, a CDN, ...), the address on the connection itself is usually the proxy's, and the real client IP arrives in the X-Forwarded-For header.

That header is only trustworthy when the peer that sent it is your own infrastructure — anyone else can write whatever they want into it. So TPS keeps a list of trusted peers, and it's the equivalent of Caddy's trusted_proxies setting:

  • A request arriving from a trusted address gets its client IP from X-Forwarded-For, walking past any other trusted hops in the chain.
  • A request arriving from anywhere else is its own client: the connection's address is used and forwarded headers are ignored.

Configuring it

As of v3.1.0, the list is configurable via the TRUSTED_PROXIES environment variable — a comma-separated list of IP addresses and/or CIDR ranges. See env-example for the full documentation. Before v3.1.0 the default below was hardcoded.

Default (unset): loopback plus the private ranges — 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, ::1/128, fc00::/7. This covers simple deployments where the front proxy reaches TPS over localhost or a Docker/private network, with no configuration needed.

Narrowing: the default trusts every host on your private network, which means any of them could spoof client IPs to TPS — including dodging a bypass key's CIDR restriction. Setting TRUSTED_PROXIES to your front proxy's actual address (e.g. TRUSTED_PROXIES="10.1.2.3") closes that off. This is the same hardening Caddy's docs recommend for its own trusted_proxies.

Widening: if a hop reaches TPS from outside the private ranges, its forwarded headers won't be honored and TPS will see that hop's address as the client. The common case is a CDN in front of your front proxy: Caddy appends the CDN edge's (public) address to X-Forwarded-For, so TPS stops there and treats the edge IP as the client. List the CDN's egress ranges alongside your front proxy's address to walk past it. The special entry private_ranges expands to everything the default trusts (just like Caddy's own private_ranges), so the easy version is TRUSTED_PROXIES="private_ranges,<CDN egress ranges>" rather than spelling the private networks back out.

Trusting no one: the special value TRUSTED_PROXIES=none ignores forwarded headers entirely; every client is its connection's address. Unlike private_ranges, none must be the entire value — none next to other entries is a contradiction, and TPS refuses to start rather than guess.

What happens if it's wrong

TPS still works — a wrong client IP degrades things rather than breaking them, which is worth understanding before you panic about misconfiguration:

  • With the request budget enabled (the default), the IP is a surcharge, not a hard binding, and the User-Agent binding still applies. If every client appears to come from one proxy IP, bots just stop paying the IP-switch surcharge.
  • Bypass keys' CIDR restrictions check the derived client IP, so they're only as good as this setting.
  • The event log and tps key list usage numbers record the derived IP, so analytics get muddy.

Clone this wiki locally