X-Forwarded-For: the client address behind a proxy
X-Forwarded-For carries the chain of client addresses a request passed through. It is a de facto standard, and every entry is forgeable unless a proxy you control wrote it.
At a glance
- Direction
- Request, added and extended by proxies.
- Defined in
- No RFC. A de facto convention, with RFC 7239 defining Forwarded as its replacement.
- When absent
- The connecting address is the client, unless something in front of you is quietly not adding the header.
- Format
- Comma separated addresses, oldest first: client, proxy1, proxy2
- Trustworthy part
- Only the entries appended by proxies you control
- Correct read
- Count back from the right by the number of trusted hops
- Standard replacement
- Forwarded, with a for parameter
In detail
Each proxy appends the address it received the connection from, so the list grows left to right and the leftmost entry claims to be the original client. That claim is the problem: a client can send the header itself, and every proxy in front of you will politely append to whatever was already there rather than discarding it.
The rule that works is to count hops from the right. If you know there are exactly two proxies you control between the internet and your application, the address of interest is the third from the end, and everything to the left of it was supplied by somebody else. Reading the first entry unconditionally means accepting whatever the client typed.
Getting this wrong is not academic. Rate limiting keyed on a forged leftmost address can be bypassed by rotating the value, and it can also be abused to block someone else by sending their address deliberately. Audit logs built from the same field inherit exactly the same problem, and they are usually the evidence you reach for afterwards.
Behind a proxy that does not add the header, everything appears to come from one address instead. That is the opposite failure and it is just as damaging: a single shared limit for every visitor, and access logs where every request comes from your own load balancer.
Examples
X-Forwarded-For: 203.0.113.7, 198.51.100.1, 198.51.100.9
X-Forwarded-Proto: https
Host: example.comWith two proxies you control, the trustworthy client address is 203.0.113.7 only because both of them appended honestly.
X-Forwarded-For: 10.0.0.1, 203.0.113.7The client sent the first entry itself. Your edge then appended the real address after it.
Things that catch people out
- Taking the leftmost entry as the client address accepts a value the client chose, which defeats any rate limit or block list built on it.
- Configuring a trusted hop count and then adding a content delivery network in front changes the number of hops and silently shifts which entry is correct.
- IPv6 addresses in this header are unbracketed by convention, unlike the Forwarded header, so a shared parser gets one of the two wrong.
Try it: HTTP Headers Checker
Check a live URL or paste headers you already have. It grades six security headers from A+ to F and lists every other header the response returned.
HTTP Headers Checker
LOCAL · Browser-OnlyView HTTP response headers for any URL
Enter any website URL to fetch and analyze its HTTP response headers. We'll automatically add https:// if needed.
Why Check Security Headers?
Security headers protect your website against common attacks like XSS, clickjacking, and MIME sniffing. They're set by your web server and cost nothing to implement. Missing security headers are one of the most common findings in security audits.
Related HTTP headers
All http headers →Forwarded: the standardised proxy header
The syntax of the standard Forwarded header, how it replaces the X-Forwarded set, how to quote IPv6 addresses and ports, and how far to trust it.
X-Forwarded-Proto: the scheme the client actually used
Why an application behind a proxy cannot see the client scheme, how X-Forwarded-Proto restores it, and the redirect loop that follows from ignoring it.
Via: the proxies a message passed through
How Via records each intermediary, what the protocol and pseudonym fields mean, how it detects request loops, and why it is useful when debugging a cache.
Host: which site the request is for
Why Host is mandatory in HTTP/1.1, how it maps to the authority in HTTP/2 and HTTP/3, and why trusting it to build URLs creates a real vulnerability.
Connection: keep-alive, close and hop by hop headers
What the Connection header controls, how it lists hop by hop headers, why HTTP/1.1 keeps connections open by default, and why HTTP/2 forbids it.
Upgrade: switching protocols on an open connection
How the Upgrade header negotiates a protocol switch, what a 101 response means, why proxies break WebSocket handshakes, and what HTTP/2 does instead.
Tools for this
- HTTP Headers Checker Free online HTTP headers checker - view HTTP response headers for any URL
- HTTP Request Builder Free online HTTP request builder - build and test HTTP requests with custom headers and body