The X-Forwarded-For
(XFF) header is a de facto standard for identifying originating IP addresses when requests pass through proxies or load balancers. The header can indeed contain multiple IP addresses in a comma-separated format:
X-Forwarded-For: client_ip, proxy1_ip, proxy2_ip
Each intermediary that handles the request may append the connecting client's IP address to the header:
- Original client: 203.0.113.42 connects to first proxy
- First proxy adds header: X-Forwarded-For: 203.0.113.42
- Request reaches second proxy (192.0.2.12) which appends the first proxy's IP
- Final header becomes: X-Forwarded-For: 203.0.113.42, 192.0.2.12
Here's how to properly parse multiple IPs in Express:
app.use((req, res, next) => {
const xForwardedFor = req.headers['x-forwarded-for'];
const ips = xForwardedFor
? xForwardedFor.split(',').map(ip => ip.trim())
: [];
// The client IP is typically the first in the list
const clientIp = ips.length > 0 ? ips[0] : req.connection.remoteAddress;
console.log('Client IP:', clientIp);
console.log('Full proxy chain:', ips);
next();
});
Important caveats when working with XFF:
- Headers can be spoofed - never trust it for security decisions without validation
- The right-most IP is most trustworthy (last proxy that appended its value)
- In AWS ALB/ELB environments, only trust the last IP when you know all intermediaries
A production request might show:
X-Forwarded-For: 79.124.62.11, 54.239.98.222, 10.0.1.42
In this case:
- 79.124.62.11 - End user's public IP
- 54.239.98.222 - CloudFront edge location
- 10.0.1.42 - Internal load balancer
The X-Forwarded-For
(XFF) HTTP header is a de facto standard for identifying the originating IP address of a client connecting to a web server through an HTTP proxy or load balancer. This header can indeed contain multiple IP addresses, separated by commas.
When a request passes through multiple proxies or intermediaries, each proxy appends the client's IP address that it received the request from. The format follows this pattern:
X-Forwarded-For: client, proxy1, proxy2
A real-world example might look like:
X-Forwarded-For: 203.0.113.42, 198.51.100.10, 192.0.2.43
Here's how you might parse XFF headers in different languages:
Node.js Implementation
function getClientIP(req) {
const xff = req.headers['x-forwarded-for'];
if (!xff) return req.connection.remoteAddress;
return xff.split(',')[0].trim();
}
Python Implementation
from flask import request
def get_client_ip():
if x_forwarded_for := request.headers.get('X-Forwarded-For'):
return x_forwarded_for.split(',')[0]
return request.remote_addr
Important points to consider when working with XFF:
- The rightmost IP is the most recent proxy
- The leftmost IP is the original client (but can be spoofed)
- Never trust XFF headers without proper verification
Some modern proxies use different headers:
Forwarded: for=192.0.2.43;proto=https;by=203.0.113.44