Technology 6 min read

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.

Caddy reverse proxy client IP header propagation diagram
Flow of visitor client IP addresses through edge routing to application backends.

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:

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:

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.

Sources and technical documentation