Run behind another proxy
When ReverseDock sits behind a load balancer, a CDN or another proxy, every connection comes from that proxy. The client’s address is only in headers such as X-Forwarded-For, which any client can also send. ReverseDock believes those headers only from the proxies you list in security.trusted_proxies.
What you need
Section titled “What you need”- The addresses or networks the proxy in front connects from. For a cloud load balancer, this is usually the subnet it runs in.
The configuration
Section titled “The configuration”listeners: web: { address: ":80" }
defaults: listeners: [web] tls: off
routes: app: match: "Host(`app.example.com`)" service: app
services: app: backends: [{ address: "10.0.0.5:3000" }]
security: trusted_proxies: [10.0.100.0/24, 192.0.2.10]Here a load balancer in 10.0.100.0/24 terminates TLS and forwards plain HTTP to port 80. Entries are IP addresses or CIDR networks, IPv4 or IPv6.
Which client IP is used
Section titled “Which client IP is used”For each request ReverseDock works out one client IP:
- The connection comes from an untrusted address: that address is the client. Forwarding headers in the request are ignored.
- The connection comes from a trusted proxy: ReverseDock reads
X-Forwarded-Forfrom right to left, skips addresses that are themselves trusted proxies, and takes the first one that is not. If every entry is trusted, the left-most one is used. At most 32 entries are examined, and an entry that is not an IP address ends the search.
Reading from the right matters: a client can put anything at the left of X-Forwarded-For, but the entries added by your own proxies are on the right.
security.client_ip_header selects the source:
| Value | Client IP from a trusted proxy |
|---|---|
auto (default) |
CF-Connecting-IP when the connection comes straight from Cloudflare (cloudflare in trusted_proxies), otherwise X-Forwarded-For, as above |
x-forwarded-for |
X-Forwarded-For, as above |
x-real-ip |
X-Real-IP sent by the proxy |
cf-connecting-ip since v0.3 |
CF-Connecting-IP sent by the proxy; needs cloudflare in trusted_proxies |
When the header is missing or is not an IP address, x-real-ip and cf-connecting-ip use the address of the connection, and auto falls back to X-Forwarded-For.
This client IP is the one used by the ClientIP rule matcher, by ip_allow, ip_deny, rate_limit with key: client_ip, by the allow list of maintenance, and in the client_ip field of the access log. Without trusted proxies, all of these see the proxy’s address, and a rate limit would throttle every client together.
What the backend receives
Section titled “What the backend receives”From an untrusted peer, ReverseDock removes these request headers, including spellings with _ instead of -, and then writes its own:
X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port, X-Forwarded-Prefix, X-Forwarded-Server, X-Forwarded-Uri, X-Forwarded-Method, X-Forwarded-Tls-Client-Cert, X-Forwarded-Tls-Client-Cert-Info, X-Real-IP, Forwarded, CF-Connecting-IP.
From a trusted proxy, these headers are kept.
In both cases ReverseDock then:
- appends the address of the connection to
X-Forwarded-For(198.51.100.7, 10.0.100.4when the proxy at10.0.100.4sentX-Forwarded-For: 198.51.100.7); - sets
X-Real-IPto the client IP worked out above; - sets
X-Forwarded-Proto(http,https, orws/wsson WebSocket handshakes),X-Forwarded-HostandX-Forwarded-Portwhen the request does not already have them. From a trusted proxy that terminated TLS, itsX-Forwarded-Proto: httpsis kept, so the application knows the client used HTTPS.
An incoming X-Request-ID is also kept only from trusted proxies (visible ASCII, at most 128 bytes); otherwise ReverseDock creates a new ID.
Check that it works
Section titled “Check that it works”Send a request through the proxy in front and look at the access log:
curl -s -o /dev/null https://app.example.com/The client_ip field of the access log line should be your own public address, not the proxy’s. A backend that echoes request headers shows X-Real-IP with the same address.
A request sent to ReverseDock directly, bypassing the proxy, with a forged header must not change the client IP:
curl -s -o /dev/null -H 'X-Forwarded-For: 203.0.113.99' http://10.0.0.2/ -H 'Host: app.example.com'Its access log line shows the address you connected from, not 203.0.113.99.
Cloudflare
Section titled “Cloudflare”The value cloudflare in trusted_proxies since v0.3 trusts Cloudflare’s edge without a list of networks to maintain:
security: trusted_proxies: [cloudflare] client_ip_header: cf-connecting-ip- The ranges stay current on their own. Cloudflare publishes them at cloudflare.com/ips and through its API (checked 2026-10-28). ReverseDock fetches
https://api.cloudflare.com/client/v4/ipsonce a day and keeps the last list in$RDOCK_DATA_DIR/cloudflare/ips.json. - Startup never waits for the network. Before the first request, ReverseDock uses the cached list, or a list built into the binary when there is no cache. A failed fetch, or an answer that does not look like Cloudflare’s list, keeps the list in use and is tried again after an hour; the log says
cannot fetch Cloudflare's IP ranges. - Nothing is fetched unless the configuration contains
cloudflare.
The client IP then comes from CF-Connecting-IP, which “provides the client IP address connecting to Cloudflare to the origin web server” (Cloudflare HTTP headers, checked 2026-10-28). client_ip_header: cf-connecting-ip needs trusted_proxies: cloudflare for proxied records, or the network of cloudflared behind a Cloudflare Tunnel. With the default auto, CF-Connecting-IP is used when the connection comes straight from a Cloudflare address, and X-Forwarded-For otherwise. From an address outside Cloudflare’s ranges (and outside your own trusted_proxies), the header is removed and ignored, so a client that bypasses Cloudflare cannot choose its IP.
If a load balancer of yours sits between Cloudflare and ReverseDock, add its network next to cloudflare. With auto, the X-Forwarded-For walk skips both the load balancer and Cloudflare’s hops; with cf-connecting-ip, the load balancer must pass Cloudflare’s header through unchanged.
The access log records the CF-Ray header of each request as cf_ray: the Ray ID that identifies the request at Cloudflare (same page).
PROXY protocol
Section titled “PROXY protocol”Load balancers that pass the client address with the PROXY protocol instead of headers are not supported yet planned for v0.6. Configure such a load balancer to send X-Forwarded-For.