nginx多层代理下获取真实客户端ip需三步:首层代理必须用proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for追加ip;在http/server块配置set_real_ip_from指定可信代理段、real_ip_header x-forwarded-for、real_ip_recursive on;最终allow/deny才作用于还原后的真实ip。

IP访问控制在Nginx中看似只靠allow/deny几行配置,但一旦前面有多层代理,真实客户端IP就很容易“失踪”。结果是规则写对了,却拦不住恶意请求,也放过了本该拒绝的地址——问题不在规则本身,而在IP根本没传到Nginx手里。
确认真实IP是否抵达Nginx最外层
这是整个链条的起点。如果第一跳(比如CDN、WAF或负载均衡器)压根没把用户真实IP放进请求头,后面所有动作都无效。
- 用
curl -v http://your-domain.com直接访问入口,看响应头里有没有X-Forwarded-For,且第一个IP是你预期的公网地址(如203.208.60.12) - Cloudflare用户要额外检查是否启用了“True Client IP”功能,并在Nginx中配置对应信任段和头字段:
set_real_ip_from 173.245.48.0/20;等(以官方最新IP段为准),real_ip_header CF-Connecting-IP; - 如果是四层代理(如启用Proxy Protocol),Nginx需配置
set_real_ip_from+real_ip_header proxy_protocol
检查中间每一层是否追加而非覆盖X-Forwarded-For
常见错误是某一层代理用$remote_addr直接覆盖X-Forwarded-For,导致原始IP被抹掉。
- 每层Nginx的
location或upstream块中,必须写:proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
注意是_add_,不是$http_x_forwarded_for或$remote_addr - HAProxy需开启
option forwardfor;Envoy需设xff_num_trusted_hops为实际跳数 - 临时加日志记录每层收到的
$http_x_forwarded_for值,对比是否连续、有无被截断成单个内网IP(如10.10.20.3)
验证Nginx是否真正“认出”了真实IP
即使头传到了,Nginx默认仍用$remote_addr(即直连上一跳的IP)做判断。你得让它主动还原。
- 在
http或server块顶部(不能放在location里)添加: -
set_real_ip_from 10.0.0.0/8;(填你实际可信代理的网段) -
real_ip_header X-Forwarded-For;(与上游发送的头一致) -
real_ip_recursive on;(多层时取最左真实IP,否则可能取到中间跳) - 加日志格式验证:
log_format debug '$remote_addr | $http_x_forwarded_for | $realip_remote_addr - "$request" $status';
重启后看日志:若$remote_addr和$realip_remote_addr都是真实公网IP,说明还原成功
确保访问控制规则作用于还原后的IP
Nginx的allow/deny指令只认$remote_addr。只要上面三步做完,$remote_addr就是真实IP,规则自然生效。
- 不要试图在
if里读$http_x_forwarded_for来判断——不安全也不可靠 - 测试时可用
curl -H "X-Forwarded-For: 1.2.3.4" http://your-domain.com模拟,但前提是该IP在set_real_ip_from声明的可信段内,否则会被忽略 - 若仍失效,检查是否有其他模块(如第三方限流模块)绕过
real_ip_module直接读$remote_addr











