Understanding Multiple IPs in X-FORWARDED-FOR: Header Parsing and Use Cases


1 views

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:

  1. Original client: 203.0.113.42 connects to first proxy
  2. First proxy adds header: X-Forwarded-For: 203.0.113.42
  3. Request reaches second proxy (192.0.2.12) which appends the first proxy's IP
  4. 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:

  1. 79.124.62.11 - End user's public IP
  2. 54.239.98.222 - CloudFront edge location
  3. 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