n8n · troubleshooting · reproduced on n8n 2.41.3, 30 September 2026
n8n X-Forwarded-For trust proxy error
If n8n’s log shows “ValidationError: The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default)”, n8n is behind a reverse proxy but has not been told so. The fix is one variable: N8N_PROXY_HOPS=1 for one proxy, or the number of proxies you run. n8n passes it to Express as trust proxy. The log line is only a warning and your requests still work. What it warns about is real, though: until you set it, every user shares the proxy’s IP for n8n’s login and password-reset rate limits.
The exact message
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. See
https://express-rate-limit.github.io/ERR_ERL_UNEXPECTED_X_FORWARDED_FOR/ for more
information.
at Object.xForwardedForHeader (…/express-rate-limit/dist/index.cjs:187:13)
…
code: 'ERR_ERL_UNEXPECTED_X_FORWARDED_FOR', Printed by n8n 2.41.3 on 30 September 2026 for the first POST /rest/login that carried an X-Forwarded-For header. It is logged once per limiter: three more identical requests added no new lines. The login request itself returned n8n’s normal 401 for a wrong password.
Two conditions have to hold, which is why some installs never show it:
- A proxy adds X-Forwarded-Fornginx, Caddy, Traefik or a cloud load balancer in front of n8n. n8n’s own reverse-proxy docs tell you to forward
X-Forwarded-For,X-Forwarded-HostandX-Forwarded-Proto, so a correct proxy setup is exactly what triggers it. - n8n runs in production modeIn 2.41.3 the per-IP limits are only attached when
NODE_ENV=production, which the official Docker image sets in its Dockerfile. The same npm install started withoutNODE_ENVlogged nothing for the identical request.
What actually breaks
With trust proxy false, Express takes the client address from the TCP connection. Behind a proxy that address is always the proxy’s, so n8n counts every user as one client. These are the per-IP limits we read in the 2.41.3 source:
| Route | Per-IP limit |
|---|---|
POST /rest/login | 1,000 requests per 5 minutes per IP (plus 5 per minute per login name) |
GET /rest/resolve-password-token, POST /rest/change-password | 5 requests per 5 minutes per IP |
MFA disable, change-email confirmation routes | 5 requests per 5 minutes per IP |
The small ones bite first. Six calls to the password-reset token check from one address:
400 400 400 400 400 429
{"message":"Too many requests"} Five 400s for a made-up token, then 429. Behind an unconfigured proxy, a few password resets in the same five minutes can use up that allowance for every user at once.
The fix: N8N_PROXY_HOPS
The n8n docs list N8N_PROXY_HOPS as “Number of reverse-proxies n8n is running behind”, default 0. In 2.41.3, any value above 0 is passed to Express as app.set('trust proxy', hops). Set it next to the rest of your proxy settings. n8n’s reverse-proxy page pairs it with the webhook URL:
# one proxy (nginx / Caddy / Traefik) in front of n8n
N8N_PROXY_HOPS=1
N8N_WEBHOOK_URL=https://n8n.example.com/
# docker run
-e N8N_PROXY_HOPS=1 -e N8N_WEBHOOK_URL=https://n8n.example.com/ With N8N_PROXY_HOPS=1, the request that had produced the ValidationError on our instance produced no log line. Recreate the container after changing the variable. A restart of the old container keeps the old environment.
How many hops?
Count the proxies you control between the internet and n8n. Express’s definition of a numeric value is “the address that is at most n number of hops away from the Express application”, read from the right of X-Forwarded-For:
- 1One reverse proxy on the same host or network: nginx, Caddy, Traefik, or a single cloud load balancer.
- 2Two in a row, each appending to
X-Forwarded-For, for example a load balancer in front of an nginx ingress. - 0 (default)n8n faces the internet directly. Leave it at 0. Trusting hops that do not exist lets clients spoof their address past the rate limits.
The other proxy settings people miss
The same proxy setup causes two other common problems. Webhook URLs that still say localhost are covered in why n8n shows a localhost webhook URL. nginx refusing large bodies with 413 Request Entity Too Large is a separate limit. If the editor refuses to load over plain HTTP, that is the secure cookie warning.
Sources and last verified
Reproduced on n8n 2.41.3, installed from npm on Node 24, on 30 September 2026; every doc quote was read on the same day. If n8n’s docs and this page disagree, n8n wins. Write to support@flowtemplates.app and we will re-verify.
- n8n Docs — Configure webhook URLs with reverse proxy (N8N_PROXY_HOPS=1 and the X-Forwarded-* headers) — checked 2026-09-30
- n8n Docs — Deployment environment variables (N8N_PROXY_HOPS, default 0) — checked 2026-09-30
- Express — Express behind proxies (what a numeric trust proxy value means) — checked 2026-09-30
- express-rate-limit — ERR_ERL_UNEXPECTED_X_FORWARDED_FOR (the error’s own help link) — checked 2026-09-30
Frequently asked questions
What does “The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false” mean in n8n?
A reverse proxy is adding an X-Forwarded-For header, but n8n has been told it is not behind a proxy, which is the default. The message comes from express-rate-limit, the library n8n uses to rate-limit its login and account endpoints. It is warning that it cannot tell your users apart: every request appears to come from the proxy’s own address.
Is the X-Forwarded-For ValidationError harmful, or just noise?
The log line itself is harmless: it is printed once and the request is answered normally. On our instance the login attempt that triggered it still returned the usual 401 for a wrong password. The misconfiguration behind it does matter. All users share one rate-limit bucket, the proxy’s IP. The password-reset and MFA routes allow 5 requests per 5 minutes per IP, so a handful of users behind one proxy can lock each other out. We got a 429 “Too many requests” on the sixth call from one address.
What should N8N_PROXY_HOPS be set to?
The number of reverse proxies between the internet and n8n. The n8n docs define it as “Number of reverse-proxies n8n is running behind”, default 0, and tell you to set it to 1 for the usual single nginx, Caddy or Traefik in front of n8n. If traffic passes through two proxies you control, say a load balancer and then nginx, use 2. n8n passes the number straight to Express, where a number means “use the address that is at most n number of hops away”.
Why do I only see this error in Docker and not with npm?
Because n8n only applies these IP rate limits in production mode, and the official Docker image sets NODE_ENV=production in its Dockerfile. On an npm install of 2.41.3 without NODE_ENV, the same request produced no message at all. With NODE_ENV=production, the first login request carrying X-Forwarded-For logged the ValidationError.
Can I just set N8N_PROXY_HOPS to a big number to be safe?
No. Trusting more hops than you really have lets a client put any address it likes at the front of X-Forwarded-For and dodge the per-IP limits. Express warns that when trusting proxies, the last trusted proxy must remove or overwrite the X-Forwarded-* headers. Set the exact number of proxies you run. Leave it at 0 if n8n faces the internet directly.