要让nginx在多级代理下准确还原客户端真实ip,必须结合可信代理白名单(set_real_ip_from)与递归提取(real_ip_recursive on),并规范透传x-forwarded-for、x-real-ip和x-forwarded-proto头;后端还需显式信任这些头,否则仍会获取到代理ip。

要让 Nginx 在多级代理环境下准确还原客户端真实 IP,不能只靠简单转发 X-Forwarded-For,必须结合可信代理识别与头字段规范透传。核心是两件事:前端代理正确追加链路信息,后端 Web 服务(或最后一级 Nginx)可靠提取最左合法 IP。
一级到 N 级代理:统一用 $proxy_add_x_forwarded_for 追加
每一级 Nginx 的 location 块中,都必须设置:
-
proxy_set_header X-Real-IP $remote_addr;—— 记录当前跳的直连客户端 IP(对上一跳而言) -
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;—— 自动拼接已有 XFF 值 + 当前 $remote_addr,用逗号分隔 -
proxy_set_header X-Forwarded-Proto $scheme;—— 透传原始协议,避免后端误判 HTTPS
例如,用户 IP 是 203.0.113.45,经 CDN(198.51.100.1)→ LB(203.0.113.10)→ Nginx(203.0.113.20)→ 应用,最终 X-Forwarded-For 头内容为:
203.0.113.45, 198.51.100.1, 203.0.113.10
最后一级 Nginx:用 real_ip_module 提取可信真实 IP
仅靠读 X-Forwarded-For 第一个值不安全(可能被伪造)。应在直接面向应用的 Nginx 节点启用 real_ip 模块,白名单所有上游可信代理段:
-
set_real_ip_from 198.51.100.0/24;—— CDN 网段 -
set_real_ip_from 203.0.113.0/24;—— 内部 LB 和中间 Nginx 网段 -
real_ip_header X-Forwarded-For;—— 指定从哪个头取链路 IP 列表 -
real_ip_recursive on;—— 启用递归剔除:从右往左,逐个比对是否在 set_real_ip_from 列表中,直到遇到第一个不在列表中的 IP,即为真实客户端 IP
配置生效后,$remote_addr 变量将自动变为 203.0.113.45,Nginx 日志、限流、访问控制等均可直接使用该变量。
后端应用必须主动信任并解析这些头
Nginx 只负责“写入”,后端不读取就等于没传。常见框架需显式配置:
-
Spring Boot:设
server.forward-headers-strategy=framework,并确保 Tomcat 启用setRemoteIpHeader("X-Forwarded-For") -
Flask:用
ProxyFix中间件,指定num_proxies=3或trusted_hosts=["198.51.100.1", "203.0.113.10"] -
PHP / Node.js / Django:禁止调用
$_SERVER['REMOTE_ADDR']或req.ip原生值,应优先读X-Real-IP, fallback 到X-Forwarded-For的第一个非私有 IP
验证是否生效的实操方法
用 curl 模拟多层链路测试:
curl -H "X-Forwarded-For: 192.0.2.100, 198.51.100.1" http://your-domain/test- 检查 Nginx access.log 是否记录
192.0.2.100为 $remote_addr(而非 198.51.100.1) - 检查后端日志或响应体,确认获取到的客户端 IP 是
192.0.2.100
若看到多个内网 IP 连续出现,说明 real_ip_recursive 未开启或 set_real_ip_from 网段漏配;若始终显示 Nginx 自身 IP,说明后端未启用 forward headers 支持。











