了解 X-Forwarded-For 和 Forwarded 头在现代 Web 架构中的重要性

发布: (2025年12月11日 GMT+8 13:28)
5 min read
原文: Dev.to

Source: Dev.to

Introduction

在 Web 早期,每个请求都直接从客户端发送到服务器,服务器可以读取 TCP 连接并知道是谁在调用。现代架构会插入多个中间层——CDN、负载均衡器、API 网关、反向代理——导致原始客户端 IP 丢失。等请求到达你的应用时,通常只能看到最后一个代理的 IP。这会让日志记录、安全、限流、地理位置定位以及故障排查变得更加困难。

转发头(X-Forwarded-For 和 Forwarded)就是为在这些跳转之间保留客户端身份而创建的。

How X-Forwarded-For Works

X-Forwarded-For(XFF)是公开来源 IP 的事实标准。每个代理都会在收到请求的 IP 地址后追加,形成一个逗号分隔的列表:

X-Forwarded-For: 203.0.113.50, 198.41.215.10
  • 最左侧的值是原始客户端。
  • 最右侧的值是最近的代理。

当涉及多个代理时,头部会一步步增长,应用程序可以据此重建完整的路径。

The Standard Forwarded Header (RFC 7239)

Forwarded 头使用键值语法形式化了相同的思路:

Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43

支持的参数:

ParameterMeaning
for客户端地址
proto原始协议(httphttps
by代理标识
host原始 Host 头部

多个代理之间用逗号分隔,降低歧义并允许附加可选元数据。

Security Considerations

因为客户端可以伪造任何头部,盲目信任转发值是危险的。更安全的策略:

  1. 从已知的代理 IP 段信任转发头。
  2. 验证每一跳,在遇到第一个不受信任的地址时停止——这就是实际的客户端。

Trusted Parsing Example (Python)

import ipaddress

TRUSTED_PROXIES = {
    ipaddress.ip_network('10.0.0.0/8'),
    ipaddress.ip_network('198.41.128.0/17')
}

def get_trusted_client_ip(xff_header: str | None, connection_ip: str) -> str:
    """
    Return the first untrusted IP from the X-Forwarded-For chain.
    If the header is missing, fall back to the direct connection IP.
    """
    if not xff_header:
        return connection_ip

    # Build the full chain: XFF entries + the immediate connection IP
    ips = [ip.strip() for ip in xff_header.split(',')]
    ips.append(connection_ip)

    # Walk the chain from the client outward, stopping at the first
    # address that is *not* in a trusted network.
    for ip_str in reversed(ips[:-1]):          # skip the last (direct) IP
        try:
            ip = ipaddress.ip_address(ip_str)
            if not any(ip in net for net in TRUSTED_PROXIES):
                return ip_str
        except ValueError:
            continue

    # All hops are trusted → the leftmost entry is the client
    return ips[0]

NGINX

# Trust internal proxy ranges
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

# Example rate‑limit zone using the resolved client IP
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

Apache

RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 10.0.0.0/8

AWS Application Load Balancer (ALB)

ALB 自动注入 X-Forwarded-ForX-Forwarded-ProtoX-Forwarded-Host。你的应用只需正确解析它们即可。

Cloudflare

Cloudflare 通过 CF-Connecting-IP 添加一个已验证的客户端地址:

CF-Connecting-IP: 203.0.113.50

Using Forwarded Headers in Application Code

Flask (Python)

from flask import request

def get_client_ip():
    xff = request.headers.get('X-Forwarded-For')
    if xff:
        # The first entry is the original client
        return xff.split(',')[0].strip()
    return request.remote_addr

Express (Node.js)

function getClientIP(req) {
    const forwarded = req.headers['forwarded'];
    if (forwarded) {
        const match = forwarded.match(/for=["']?([^"',;\s]+)/i);
        if (match) return match[1].replace(/^\[|\]$/g, '');
    }

    const xff = req.headers['x-forwarded-for'];
    if (xff) return xff.split(',')[0].trim();

    return req.socket.remoteAddress;
}

Additional Forwarded Headers

HeaderPurpose
X-Forwarded-Proto原始协议(http/https
X-Forwarded-Host原始 Host 头部
X-Real-IP简化的客户端 IP(常由 NGINX 使用)
Via显示代理链及其协议

这些头部与 X-Forwarded-For/Forwarded 互补,提供更完整的请求路径信息。

Conclusion

转发头对现代、代理密集的 Web 架构至关重要。它们恢复了对真实客户端身份的可视性,使日志、安防分析、限流、地理定位和访问控制更加精准。然而,使用时必须谨慎:仅接受可信代理发送的头部,并始终对提取的值进行验证。正确使用时,X-Forwarded-For 与标准化的 Forwarded 头部将成为强大的工具,帮助保留本可能丢失的上下文信息。

Back to Blog

相关文章

阅读更多 »