最有效做法是在 proxy_pass 前用 proxy_set_header x-real-ip ""; 清空客户端伪造头,再配合 realip 模块校验可信源,并在后端只信任 nginx 注入的 x-real-ip。

直接在 proxy_pass 前清除客户端自带的 X-Real-IP,而不是透传或覆盖它——这是最有效、最干净的做法。
用 proxy_set_header 清空原始值
客户端可能通过 curl 或工具手动带上 X-Real-IP: 1.2.3.4 这类伪造头,Nginx 默认会原样转发。要彻底阻断,不能只设新值,得显式清空:
-
正确写法:
proxy_set_header X-Real-IP "";(注意是空字符串,不是$remote_addr) - 这条指令必须放在
proxy_pass之前,且优先级高于任何其他proxy_set_header - 配合
proxy_hide_header X-Real-IP;可进一步防止后端意外读到该头(部分框架会自动解析请求头)
避免“覆盖式”误操作
很多人写 proxy_set_header X-Real-IP $remote_addr;,看似合理,实则危险:
- 如果客户端已带恶意 X-Real-IP,而 Nginx 又没清空就直接设新值,某些后端语言(如 PHP 的
$_SERVER['HTTP_X_REAL_IP'])仍可能读到原始伪造值 - 尤其在 FastCGI 或某些反向代理中间件中,头部合并逻辑不一致,旧值可能残留
- 清空 + 重设才是双保险:先
proxy_set_header X-Real-IP "";,再按需设可信值(如proxy_set_header X-Real-IP $remote_addr;)
配合 realip 模块统一信任源
仅清空 X-Real-IP 不够,还需确保 $remote_addr 本身是可信的:
- 在 http 块中启用 realip 模块,明确指定可信上游:
set_real_ip_from 10.0.0.0/8;、set_real_ip_from 192.168.0.0/16;等 - 设置
real_ip_header X-Forwarded-For;和real_ip_recursive on; - 这样
$remote_addr才是经校验的真实客户端 IP,后续设X-Real-IP $remote_addr才有意义
后端也需同步防御
前端清空只是第一道防线,后端不能无条件信任 X-Real-IP:
- 应用层应只从
X-Real-IP读取 IP,且仅当该头由 Nginx 主动注入(即非客户端原始提供) - 推荐做法:后端忽略所有客户端传入的 X-Real-IP、X-Forwarded-For,只信任 Nginx 注入的
X-Real-IP,并结合 session 中固化 IP 做一致性校验 - 敏感操作(如修改密码、支付)必须比对当前请求 IP 与登录时记录的 IP 是否一致











