Nginx传递真实IP需两步:先用set_real_ip_from和real_ip_header还原$remote_addr,再用proxy_set_header X-Real-IP $remote_addr等透传;后端必须主动读取X-Real-IP而非REMOTE_ADDR,且仅信任可信代理来源。

核心是两件事:Nginx 正确设置并传递 IP 相关请求头,后端服务主动读取并信任这些头——不能只靠 REMOTE_ADDR。
基础配置:单层代理(Nginx 是第一入口)
在 location 或 server 块中添加以下三行:
-
proxy_set_header X-Real-IP $remote_addr;—— 把直连客户端的 IP 写入该头,简洁明确 -
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;—— 自动追加,不覆盖已有值;若上游没传,就设为$remote_addr;若有,就在末尾加一个逗号再追加 -
proxy_set_header X-Forwarded-Proto $scheme;—— 同时透传原始协议(http/https),对重定向和 URL 生成很重要
进阶处理:前面还有可信代理(如 CDN、SLB、WAF)
此时 $remote_addr 是 CDN 或负载均衡器的 IP,不是用户真实 IP。需让 Nginx 主动“识别”并更新自身记录的客户端地址:
- 用
set_real_ip_from明确声明可信代理的 IP 段(例如阿里云 SLB 常用100.64.0.0/10,腾讯云 CLB 常用119.29.0.0/16) - 用
real_ip_header X-Forwarded-For;告诉 Nginx 从哪个头里提取真实 IP - 加
real_ip_recursive on;启用递归解析:从右往左剔除已知代理 IP,取第一个非代理地址作为最终$remote_addr
配置生效后,$remote_addr 就变成真实用户 IP,上面的 X-Real-IP $remote_addr 才真正可靠。
后端必须配合:不能只读 REMOTE_ADDR
Nginx 只负责“发”,后端得会“收”。常见错误是直接调用:
- Java:
request.getRemoteAddr() - Python Flask:
request.remote_addr - ThinkPHP:
request()->ip()(默认不启用代理头解析)
正确做法是优先读取 X-Real-IP, fallback 到 X-Forwarded-For 最左非私有 IP(注意过滤内网段),且只在来源可信时才采信。
- Tomcat:启用
RemoteIpValve,自动重写getRemoteAddr()行为 - Spring Boot:配
server.tomcat.remote-ip-header=x-forwarded-for等参数 - ThinkPHP:需显式配置
proxy_server_ip和proxy_server_ip_header数组
安全提醒:X-Forwarded-For 可被伪造
这个头没有强制校验机制,客户端或中间节点可随意修改。所以:
- 永远不要无条件信任
X-Forwarded-For的全部内容 - 只接受来自已知可信代理(通过
set_real_ip_from白名单)转发过来的该头 - 后端做 IP 判断时,应结合
X-Real-IP(由 Nginx 控制)+ 可信链路验证


















