devkult_
tools29converters34
home/headers/x-forwarded-for

X-Forwarded-For

request headerrouting & context

The chain of client IP addresses a request passed through on its way via proxies and load balancers.

Behind a proxy, the connection your app sees comes from the proxy, not the user. Each proxy appends the address it received the request from, so the header reads client, proxy1, proxy2. The catch: clients can send a fake X-Forwarded-For, so only the entries added by proxies you control can be trusted.

Syntax

X-Forwarded-For: <client>, <proxy1>, <proxy2>

Defined in RFC 7239 — Forwarded HTTP Extension.

Examples

X-Forwarded-For: 203.0.113.195

one proxy in front of the app

X-Forwarded-For: 203.0.113.195, 198.51.100.17

the client went through two proxies

Good to know

  • Read from the right: count back past the proxies you run. The leftmost entry is whatever the client claimed.
  • In Express, set app.set("trust proxy", 1) (the number of proxy hops) so req.ip uses this header correctly.
  • The standard replacement is Forwarded: for=203.0.113.195;proto=https, but X-Forwarded-For remains far more common.
  • Related de-facto headers: X-Forwarded-Proto (original scheme) and X-Forwarded-Host (original Host).

Further reading: Fixing ERR_TOO_MANY_REDIRECTS

Who sets X-Forwarded-For

Proxies, load balancers and CDNs add X-Forwarded-For; your app only reads it. In nginx: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;. Vercel and Cloudflare set it for you, and Vercel also sends the client IP in x-real-ip.

Check it

In the browser, open DevTools → Network, pick a request and look under Request Headers. From the command line, curl's verbose mode prints every header it sends, prefixed with >:

curl -sv https://example.com/ -o /dev/null 2>&1 | grep '^>'

Frequently asked questions

Can I use X-Forwarded-For for rate limiting?

Only the entries added by your own proxies. If you trust the leftmost value, anyone can bypass the limit by sending a different fake IP each time.

Related headers