--- title: "n8n Behind Reverse Proxy: X-Forwarded-For Trust Fix" domain: troubleshooting category: docker tags: [n8n, caddy, reverse-proxy, docker, express] status: published created: 2026-04-02 updated: 2026-04-02 --- # n8n Behind Reverse Proxy: X-Forwarded-For Trust Fix ## The Problem When running n8n behind a reverse proxy (Caddy, Nginx, Traefik), the logs fill with: ``` ValidationError: The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users. ``` This means n8n's Express rate limiter sees every request as coming from the proxy's internal IP, not the real client. Rate limiting and audit logging both break. ## Why `N8N_TRUST_PROXY=true` Isn't Enough Older n8n versions accepted `N8N_TRUST_PROXY=true` to trust proxy headers. Newer versions (1.x+) use Express's `trust proxy` setting, which requires knowing *how many* proxy hops to trust. Without `N8N_PROXY_HOPS`, Express ignores the `X-Forwarded-For` header entirely even if `N8N_TRUST_PROXY=true` is set. ## The Fix Add `N8N_PROXY_HOPS=1` to your n8n environment: ### Docker Compose ```yaml services: n8n: image: docker.n8n.io/n8nio/n8n:latest environment: - N8N_HOST=n8n.example.com - N8N_PROTOCOL=https - N8N_TRUST_PROXY=true - N8N_PROXY_HOPS=1 # <-- Add this ``` Set `N8N_PROXY_HOPS` to the number of reverse proxies between the client and n8n: - **1** — single proxy (Caddy/Nginx directly in front of n8n) - **2** — two proxies (e.g., Cloudflare → Caddy → n8n) ### Recreate the Container ```bash cd /opt/n8n # or wherever your compose file lives docker compose down docker compose up -d ``` If you get a container name conflict: ```bash docker rm -f n8n-n8n-1 docker compose up -d ``` ## Verifying the Fix Check the logs after restart: ```bash docker logs --since 5m n8n-n8n-1 2>&1 | grep -i "forwarded\|proxy\|ValidationError" ``` If the fix worked, there should be zero `ValidationError` lines. A clean startup looks like: ``` n8n ready on ::, port 5678 Version: 2.14.2 Editor is now accessible via: https://n8n.example.com ``` ## Key Notes - Keep `N8N_TRUST_PROXY=true` alongside `N8N_PROXY_HOPS` — both are needed. - The `mount of type volume should not define bind option` warning from Docker Compose when using `:z` (SELinux) volume labels is cosmetic and can be ignored. - If n8n reports "Last session crashed" after a `docker rm -f` recreation, this is expected — the old container was force-killed, so n8n sees it as a crash. It recovers automatically.