trust proxy
trust proxy decides whether Express believes X-Forwarded-For and
X-Forwarded-Proto. It defaults to false, and it has two wrong values rather than
one — which is why this page exists separately from the cookie and rate-limit pages
that both depend on it.
| Value | Result |
|---|---|
false (default) |
req.ip is the proxy. Secure cookies never send. One shared rate-limit bucket. |
true |
req.ip is whatever the client claims. Trivially spoofable. |
1 / 'loopback' / a specific address |
Correct: trust exactly the hops you operate |
// Trust exactly one proxy hop — the one you run.app.set("trust proxy", 1);The number is a hop count, not a boolean-ish. 1 means “take the last entry in
X-Forwarded-For”, which is the value your own proxy appended and the only one a
client cannot forge. If you run two proxies (a CDN in front of a load balancer), the
number is 2.
curl -sI -H 'X-Forwarded-For: 1.2.3.4' https://example.com/whoamiExpose req.ip on a debug route temporarily, or read it from your access logs. If a
forged header changes what the application records, the setting is too permissive.
The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.express-rate-limit refuses to treat `true` as a valid production configuration, because it makes req.ip attacker-controlled.
Read in: express-rate-limit source/validations.ts — ERR_ERL_PERMISSIVE_TRUST_PROXYThe '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.A proxy is in front of the app but Express does not know it, so req.ip is the proxy's address and every visitor shares one rate-limit bucket.
Read in: express-rate-limit source/validations.ts — ERR_ERL_UNEXPECTED_X_FORWARDED_FORtrue is the dangerous one, and a real library refuses it
Section titled “true is the dangerous one, and a real library refuses it”trust proxy: true means “trust the whole chain”, so Express takes the leftmost
entry of X-Forwarded-For — the part a client wrote. Anyone can then present any IP
they like.
express-rate-limit treats this as a configuration error rather than a preference, and its message says why: it “allows anyone to trivially bypass IP-based rate limiting.” That is a library declining to operate on a setting it considers unsafe, which is about as clear a signal as this ecosystem gives.
Note that both failure directions are silent in Express itself. Nothing in Express warns on either value; the warnings above come from express-rate-limit noticing.
What depends on this
Section titled “What depends on this”Three separate controls quietly rest on trust proxy being right:
- Secure cookies —
secure: trueneedsX-Forwarded-Prototo be believed, or the cookie is never sent. See session cookies. - Rate limiting — keyed on
req.ip. Too strict and everyone shares one bucket; too permissive and the limit is bypassable. See rate limiting. - Anything IP-based — audit logs, allow-lists, geo rules, abuse heuristics.
That shared dependency is why the threat page for this cluster is about this setting rather than about a vulnerability.
Changing this changes what every downstream consumer of req.ip sees, including your
logs. Expect the client addresses in your access logs to change the moment you deploy
it — that is the fix working, but it will look like a logging bug if nobody is warned.
The setting is only as trustworthy as the proxy. Express believing the last hop is
correct only if that proxy overwrites or appends X-Forwarded-For itself. A proxy
that passes a client-supplied header through untouched makes any value here a
formality.
Related
Section titled “Related”- Trust proxy spoofing — what a wrong value lets someone do
- Application vs server — why this setting is only ever as trustworthy as the proxy feeding it