Proxy applications
Preserve client identity safely
Use Caddy’s forwarded headers and trusted proxy settings without accepting forged client addresses.
Put a proxy in front of an app and the app stops seeing real client addresses. Every connection now comes from Caddy, so the app logs 127.0.0.1 for everyone. Rate limiting, audit logs, anything keyed on the client IP: all broken.
Forwarded headers
The fix is a convention called forwarded headers. On every proxied request Caddy sets X-Forwarded-For with the client’s address, X-Forwarded-Proto with the original scheme, and X-Forwarded-Host with the original host.
Your application reads those instead of the socket address. You don’t configure anything. reverse_proxy does it by default.
The forgery problem
Now the security problem. Headers are just text, and any client can send one. Let’s try to forge an address through the proxy from the last lesson:
curl -H "X-Forwarded-For: 203.0.113.9" http://127.0.0.1:8080/
Log the incoming headers in your application and look at what arrived. The forged value is gone. Caddy replaced it with the address it saw on the socket.
Why? Because the request didn’t come from a trusted proxy, so Caddy refuses to believe its identity claims. Forgery is defeated by default, without you writing a rule.
When a real proxy sits in front
Sometimes there’s a legitimate proxy in front of Caddy: a load balancer or a CDN. Then the socket address Caddy sees belongs to the CDN, and the real client address is in the header the CDN sends.
You tell Caddy which peers to believe with the global trusted_proxies option:
{
servers {
trusted_proxies static 10.0.0.0/8
}
}
Now, when a connection arrives from an address in 10.0.0.0/8, Caddy trusts its forwarded headers and passes the real client identity to your upstream. Connections from anywhere else still get the socket address.
Don’t trust too widely
The mistake to avoid is a range that’s too big. Set trusted_proxies to ranges you control, never to 0.0.0.0/0.
If everyone is a trusted proxy, anyone can claim any address. Every system downstream that keys off the client IP believes the lie: bans, throttles, audit trails. An attacker sets one header and your rate limiter thinks they’re a thousand different people.
When to look here
Two symptoms point at this setting. Your logs show impossible client addresses, like private ranges from the public Internet. Or every visitor appears to come from one CDN IP. In the first case you trust too much. In the second, you don’t trust the proxy that’s really there.
Try this on your own: repeat the forgery curl after adding 127.0.0.1/32 to trusted_proxies and reloading. The forged address now arrives at your app. That’s what a misconfigured range does to you.
Lesson completed