- trustedProxies tells the gateway which upstream IPs to trust for X-Forwarded-For headers — wrong values corrupt IP logging and rate limiting silently
- Never set trustedProxies to 0.0.0.0/0 — it allows any client to spoof their source IP and bypass rate limits entirely
- Use the internal subnet of your reverse proxy (Docker bridge, private VPC CIDR) — not the proxy's public IP
- Only CIDR notation and bare IPs are valid — hostnames will cause a parse error at startup
- After changing trustedProxies, verify by checking openclaw logs — the logged client IP should match the real client, not the proxy
Most builders add a reverse proxy in front of the OpenClaw gateway and move on. The gateway starts, agents respond, everything looks fine. Three weeks later they notice every client in their logs has the same IP address — their Nginx container's address. Rate limiting is broken. Geo-blocking doesn't work. Audit logs are useless. The cause is always trustedProxies.
What TrustedProxies Actually Does
When a request passes through a reverse proxy like Nginx or Caddy, the proxy adds an X-Forwarded-For header containing the real client's IP. By default, the OpenClaw gateway ignores this header and uses the direct connection IP — which is your proxy's IP, not the client's.
trustedProxies in gateway.yaml tells the gateway: "If a request comes from one of these IP ranges, trust the X-Forwarded-For header and use it as the real client IP." Without this, every client looks like your reverse proxy.
The consequences extend further than just logging. Rate limiting enforces per-IP thresholds. If all requests appear to come from one IP (your proxy), the rate limiter can't distinguish clients. A single bad actor could saturate your LLM API budget because they're invisible as a distinct source.
The 5 Mistakes That Break Your Stack
Mistake 1 — Setting 0.0.0.0/0
This trusts every IP on the internet as a proxy. Any client can now inject an arbitrary X-Forwarded-For header and appear to come from any IP they choose. This completely defeats IP-based rate limiting — a client can cycle through random IPs in the header and never hit a per-IP limit.
We've seen this config on production deployments where the builder was following a "quick start" blog post that used 0.0.0.0/0 for simplicity. The result: unlimited LLM API calls from a single bad actor who discovered the endpoint.
Mistake 2 — Using the Proxy's Public IP Instead of Private IP
Your Nginx server's public IP is what the internet sees. But the gateway only sees traffic from Nginx's internal network interface. In Docker setups, this is the bridge network IP (typically 172.x.x.x). Entering the public IP means trustedProxies never matches, X-Forwarded-For is ignored, and you're back to all requests appearing to come from your proxy's internal IP.
Mistake 3 — Entering a Hostname Instead of an IP
The trustedProxies field only accepts IPs and CIDR ranges. Entering nginx or proxy.internal causes a parse error at gateway startup. The gateway either fails to start or silently ignores the invalid entry, depending on the version. As of early 2025, v0.4+ logs a clear parse error — older versions may start anyway with trustedProxies effectively disabled.
Mistake 4 — Not Updating After Moving to a New Network
Docker bridge networks reassign subnets when containers are rebuilt. If you hardcoded a specific 172.18.0.x IP, it may no longer match after a docker compose down && docker compose up cycle. The subnet usually stays stable, but individual container IPs change. Use the subnet CIDR (172.18.0.0/16) rather than a specific container IP to survive rebuilds.
Mistake 5 — Trusting a Subnet That's Too Wide
Using 10.0.0.0/8 when your proxy only sits at 10.0.1.2 means any machine on your entire private network can inject X-Forwarded-For headers. In shared hosting or multi-tenant VPC environments, this allows neighbor VMs to spoof client IPs. Use the smallest CIDR that covers your actual proxy IPs.
Correct Configuration
Here's what a correct gateway.yaml trustedProxies block looks like for a Docker Compose deployment:
gateway:
host: 0.0.0.0
port: 8080
trustedProxies:
- 172.18.0.0/16 # Docker bridge subnet for this compose project
rateLimit:
enabled: true
requestsPerMinute: 30
For a VPS deployment with Nginx running on the same machine (localhost proxy):
gateway:
trustedProxies:
- 127.0.0.1 # localhost Nginx
- ::1 # IPv6 localhost
For Kubernetes with an ingress controller on a private subnet:
gateway:
trustedProxies:
- 10.96.0.0/12 # Kubernetes pod CIDR
- 192.168.0.0/16 # Private VPC subnet
docker network inspect bridge or docker network inspect [your-compose-network] to see the subnet CIDR. Use that value in trustedProxies. The subnet is stable across container restarts; individual container IPs are not.Docker and Kubernetes Specifics
Docker Compose creates a dedicated bridge network for each project. Its name is typically [project-name]_default. Containers within the same compose project communicate over this bridge.
When Nginx (in the same compose project) proxies to the OpenClaw gateway, the gateway sees traffic from Nginx's IP on that bridge network — not from Nginx's public interface. The bridge subnet is usually in the 172.16.0.0/12 range. Confirm it with docker network inspect.
For Kubernetes, your ingress controller pods run in the cluster's pod CIDR. Check your cluster's pod CIDR with kubectl cluster-info dump | grep podCIDR. The trustedProxies entry should cover that range, not the node's external IP.
Multi-Layer Proxy Chains
Some setups run Cloudflare → Nginx → OpenClaw gateway. In this case, Nginx strips Cloudflare's X-Forwarded-For and sets its own. The gateway only needs to trust Nginx's internal IP. You don't need to add Cloudflare's IP ranges to trustedProxies if Nginx is handling the forwarding header correctly.
Testing That Your Setup Is Correct
After updating trustedProxies and restarting the gateway, test with a real request through your proxy:
# From a machine with a known IP (e.g., your laptop at 203.0.113.42)
curl -H "Authorization: Bearer your-token" \
https://your-gateway.example.com/api/v1/status
# Then check gateway logs
openclaw logs --tail 20
In the logs, look for the client IP. It should show 203.0.113.42 — your laptop's real public IP. If it shows your Nginx container's IP (typically 172.x.x.x), trustedProxies is not matching and the X-Forwarded-For header is being ignored.
Sound familiar? This is the test most builders skip, and it's the first thing I check when a client reports "rate limiting isn't working."
Common Errors After Changes
If the gateway fails to start after editing trustedProxies, check openclaw logs --level error for a parse error indicating an invalid CIDR or hostname. Fix the entry and restart.
If clients start hitting rate limits they weren't hitting before — after you correct trustedProxies — that's expected. The rate limiter was previously applying limits per proxy IP (one bucket for everyone). Now it correctly applies limits per real client IP (individual buckets per client).
One more thing: some builders set trustedProxies: [] explicitly to disable proxy trust entirely. This works correctly — no X-Forwarded-For headers are trusted and the direct connection IP is always used. This is the right choice if you're running the gateway without a reverse proxy.
Frequently Asked Questions
What does trustedProxies do in OpenClaw gateway?
trustedProxies tells the gateway which upstream IP addresses are trusted reverse proxies. When set correctly, the gateway reads the real client IP from X-Forwarded-For headers. This is critical for accurate rate limiting, logging, and IP-based access checks — without it, every client appears to be your proxy.
What happens if I set trustedProxies to 0.0.0.0/0?
Setting trustedProxies to 0.0.0.0/0 trusts every IP as a proxy, which means any client can spoof their source IP via X-Forwarded-For headers. This completely bypasses IP-based rate limiting. Never use this in production — scope it to only your actual proxy IPs.
How do I find the correct IP to add to trustedProxies?
The correct IP is your reverse proxy's internal network address — typically the Docker bridge network IP. Run docker network inspect [network-name] to find the subnet CIDR. For Docker Compose setups, use the bridge subnet like 172.18.0.0/16 rather than a specific container IP to survive rebuilds.
Does trustedProxies affect WebSocket connections?
Yes. WebSocket upgrade requests also carry X-Forwarded-For headers when passing through a proxy. If trustedProxies is wrong, WebSocket client IPs are logged incorrectly. Fix trustedProxies and WebSocket IP logging corrects automatically on the next connection.
Can I use a hostname instead of an IP in trustedProxies?
No. trustedProxies only accepts CIDR notation or bare IP addresses. Hostnames cause a parse error at startup. Use the static private IP or subnet CIDR for your proxy — never a hostname or service name.
How do I test that trustedProxies is configured correctly?
Send a request through your proxy from a machine with a known IP, then check openclaw logs. The logged client IP should match your real machine IP, not your proxy's internal IP. If you see the proxy's IP for every request, trustedProxies is not applying correctly — restart after any config change.
S. Rivera designs production infrastructure for OpenClaw deployments across cloud and on-premises environments. Has debugged proxy misconfiguration issues on deployments ranging from single-VPS setups to multi-region Kubernetes clusters with Cloudflare at the edge.