Files
MajorWiki/05-troubleshooting/docker/n8n-proxy-trust-x-forwarded-for.md
majorlinux 0df5ace1a2 wiki: add n8n reverse proxy X-Forwarded-For trust fix article
Documents the N8N_PROXY_HOPS env var needed for n8n behind Caddy/Nginx
when N8N_TRUST_PROXY alone is insufficient in newer versions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 19:48:01 -04:00

2.4 KiB

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

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

cd /opt/n8n  # or wherever your compose file lives
docker compose down
docker compose up -d

If you get a container name conflict:

docker rm -f n8n-n8n-1
docker compose up -d

Verifying the Fix

Check the logs after restart:

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.