要让后端准确获取原始客户端IP,需可信链路识别、X-Forwarded-For全链路透传(首层用$remote_addr初始化,后续用$proxy_add_x_forwarded_for追加)、Nginx realip模块校验(set_real_ip_from指定可信代理,real_ip_header X-Forwarded-For,real_ip_recursive on),并确保后端框架适配透传头。

要让后端服务准确拿到最原始客户端的IP,关键不在“层层加头”,而在于可信链路识别 + 头部解析 + 后端配合。单靠X-Forwarded-For追加或X-Real-IP硬赋值,在多级代理下极易失真。
每层代理都必须透传X-Forwarded-For
这是全链路透传的基础前提。所有中间节点(包括CDN、WAF、LB、Nginx反代)都需确保:
- 首层代理(直连客户端)用
$remote_addr初始化X-Forwarded-For,写法为:proxy_set_header X-Forwarded-For $remote_addr; - 后续每一层代理不覆盖、只追加,统一使用:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
(该变量会自动拼接当前$remote_addr到已有XFF值末尾) - 禁止任意环节清空或重写X-Forwarded-For——漏掉一环,真实IP就断了
后端Web服务器启用realip模块精准提取
仅靠读取HTTP_X_FORWARDED_FOR字符串并取最左IP是危险的:攻击者可伪造该头。必须由Nginx在最终入口处完成可信校验与剥离。
在Web服务所在Nginx的http、server或location块中添加:
-
set_real_ip_from 10.0.0.5;—— 一级代理IP -
set_real_ip_from 192.168.100.0/24;—— 二级代理网段 -
set_real_ip_from 203.208.60.0/24;—— CDN回源IP段(如Google Cloud CDN) real_ip_header X-Forwarded-For;real_ip_recursive on;
启用后,Nginx会将$remote_addr重写为XFF中最右起第一个**不在可信列表中**的IP——这才是真实客户端IP。日志、PHP的$_SERVER['REMOTE_ADDR']、Java的request.getRemoteAddr()都将反映该值。
后端应用必须信任并适配透传头
Nginx只负责“还原”和“注入”,后端框架若仍按默认逻辑取连接远端地址,就前功尽弃。
-
Spring Boot:设
server.forward-headers-strategy=framework,并确认内嵌Tomcat已配置setRemoteIpHeader("X-Forwarded-For") -
Flask:用
from werkzeug.middleware.proxy_fix import ProxyFix包裹app,指定num_proxies=3或trusted_hosts=["10.0.0.5", "10.0.0.6"] -
Express:设
app.set('trust proxy', true),并确保Nginx已透传X-Forwarded-For和X-Forwarded-Proto -
PHP-FPM:无需额外代码,只要Nginx启用了realip且
fastcgi_param REMOTE_ADDR $remote_addr;(默认即如此)
验证与排障要点
上线前务必实测,避免理论正确但线上失效:
- 用curl模拟多跳请求:
curl -H "X-Forwarded-For: 203.0.113.19, 10.0.0.5, 10.0.0.6" http://your-domain.com/ip.php - 检查Nginx access日志中的
$remote_addr是否已是客户端IP(非上一跳) - 查看后端输出(如PHP中
echo $_SERVER['REMOTE_ADDR'])是否匹配预期 - 确认
set_real_ip_from未遗漏任一可信代理IP段——少写一条,就可能把某层代理IP误认为客户端


















