Caddy Reverse Proxy Client IP: Headers and Setup Guide
Caddy reverse proxy client IP rules pass visitor addresses to web apps. They also block fake headers. This guide explains basic forwarding, trusted network setup, and path access controls.
How edge routing handles visitor headers
Web apps need visitor IP addresses.
They track login attempts. They also block spam bots.
Traffic often flows through an edge gateway first. Because of this hop, the app only sees the gateway IP. To fix this, edge services pass visitor details inside request headers.
Most old tools require manual setup.
In contrast, this web server handles headers on its own. When you add a routing rule, it runs three tasks:
- It adds the visitor IP to
X-Forwarded-For. - It sets
X-Forwarded-Prototo the URL scheme. - It writes the requested domain to
X-Forwarded-Host.
Security stays tight.
When an outside visitor sends a fake header, the edge gateway drops it. It uses the real socket IP instead. This simple check blocks address spoofing.
Read our guide on caddy reverse proxy docker compose to see how bridge networks work.
Eliminating redundant header rewrite rules
Engineers frequently copy legacy configuration snippets directly from online forums without verifying modern defaults.
Stop doing this.
Those outdated forum tutorials clutter your Caddyfile with unneeded directives.
# Antipattern: avoid manual header rewrites
example.com {
reverse_proxy 127.0.0.1:8080 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
These manual directives create real operational problems in production.
Warnings appear.
The parser alerts on boot, and hardcoding {remote_host} erases upstream proxy chains while bypassing internal security checks.
Use clean syntax instead:
# Recommended production syntax
example.com {
reverse_proxy 127.0.0.1:8080
}
Check our review of caddy vs nginx reverse proxy to compare config syntax.
Configuring trusted proxies for CDNs and edge layers
What if you use a CDN?
Cloudflare receives the first hit. It passes traffic from its own edge nodes. By default, your gateway drops incoming forward headers to stay safe.
To keep visitor addresses, list trusted subnets. Add the trusted_proxies rule in global options:
# Global options with trusted networks
{
servers {
trusted_proxies static private_ranges 173.245.48.0/20 103.21.244.0/22
client_ip_headers CF-Connecting-IP X-Forwarded-For
}
}
example.com {
reverse_proxy backend:8080
}
The word private_ranges matches private subnets like 10.0.0.0/8. It also covers local host links. When traffic comes from these ranges, the server trusts the header.
| Deployment Topology | Connecting Peer | Required Directives | Visitor IP Source |
|---|---|---|---|
| Direct Public Edge | End user browser | Standard gateway block | TCP socket address |
| Behind Cloudflare | CDN edge node | servers { trusted_proxies ... } |
Parsed from CF-Connecting-IP |
| Internal Docker Network | Docker bridge host | trusted_proxies static private_ranges |
Augmented X-Forwarded-For |
| Chained Gateways | Front gateway instance | trusted_proxies static private_ranges |
Verified upstream header |
Read our guide on caddy reverse proxy websocket to inspect real-time links.
Using client IP matchers for access control
Once addresses are verified across trusted edge networks, you can securely restrict administrative routes.
Filter paths.
The client_ip matcher checks the parsed address. It works even behind a CDN.
example.com {
# Match client IP even behind a CDN
@office {
client_ip 198.51.100.42 203.0.113.0/24
}
# Block outside access to admin tools
handle /admin/* {
@blocked not client_ip 198.51.100.42
respond @blocked "Access Denied" 403
reverse_proxy admin-app:9000
}
# Serve public app traffic
reverse_proxy public-app:8080
}
Do not confuse client_ip with remote_ip.
The remote_ip check looks directly at the immediate socket layer. Behind Cloudflare, it sees Cloudflare nodes, which breaks rules. In contrast, client_ip reads the real visitor header.
Read our tutorial on caddy reverse proxy subpath to learn prefix routing.
Common production pitfalls
Avoid these common setup traps:
- Trusting All IPs: Never put
0.0.0.0/0in trusted ranges. Doing so lets bad users fake their location. - Header Precedence: Caddy evaluates headers listed in
client_ip_headerssequentially, applying the first non-empty value found. - Unix Sockets: Domain socket links skip IP checks unless you enable socket trust.
Frequently asked questions
Does Caddy forward the real client IP by default?
Yes. The gateway sets or updates the X-Forwarded-For header automatically.
Why are manual header_up directives unnecessary in Caddy?
The web server passes standard headers on its own.
How do you trust Cloudflare or upstream proxy IPs in Caddy?
Set trusted_proxies in the global options block.