关键不是直接拦$remote_addr,而是先启用realip模块:set_real_ip_from声明可信代理网段,real_ip_header指定x-forwarded-for,real_ip_recursive on递归剔除可信ip后取最左合法ip赋给$remote_addr,再基于它配置allow/deny规则。

多级代理下做IP访问控制,关键不是直接拦 $remote_addr,而是先还原真实客户端IP,再基于它做判断。否则你拦的其实是最后一级代理的内网IP,完全失效。
必须启用 realip 模块并正确配置信任链
Nginx 默认的 $remote_addr 是直连它的上一级代理IP(比如 172.16.0.48),不是用户真实IP。要让它变成真实IP,得靠 realip 模块主动“替换”:
-
set_real_ip_from:明确列出所有可信代理的IP或网段(如
172.16.0.48、192.168.0.0/16、10.0.0.0/8),只有来自这些地址的请求,才允许解析其携带的X-Forwarded-For或X-Real-IP -
real_ip_header:指定从哪个HTTP头里取IP,生产环境普遍用
X-Forwarded-For(它能承载多级IP链) -
real_ip_recursive on:开启递归过滤。比如头里是
1.2.3.4, 172.16.0.48, 192.168.1.10,Nginx会从右往左逐个比对set_real_ip_from列表,跳过所有匹配项,把第一个不匹配的IP(即最左边的1.2.3.4)赋给$remote_addr
在 location 或 server 块中写 allow/deny 规则
配置完 realip 后,$remote_addr 就是真实客户端IP了,这时的访问控制才真正有效:
- 只允许某几个公网IP访问:
allow 203.208.60.1; allow 203.208.60.2; deny all; - 只允许某个CIDR网段:
allow 203.208.60.0/24; deny all; - 注意顺序:
allow和deny按出现顺序执行,第一条匹配即生效,无需加deny all也能默认拒绝
代理链每一环都要透传 X-Forwarded-For
如果中间某一级代理没转发头,真实IP就会在那一环丢失。确保每层Nginx都配置了:
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;(推荐,自动追加,不覆盖) - 不要用
$http_x_forwarded_for,它可能为空或被篡改 - 前端如果是云WAF或CDN(如阿里云、Cloudflare),需确认它们是否默认透传该头;若不透传,需在WAF控制台开启或联系厂商
验证是否生效的简单方法
加一条日志格式,同时输出原始和还原后的IP:
log_format debug '$remote_addr | $http_x_forwarded_for | "$http_x_real_ip" - [$time_local] "$request" $status';
然后发起一次请求,查看 access.log。理想输出类似:172.16.0.48 | 203.208.60.1, 172.16.0.48 | "203.208.60.1" - [02/Oct/2026:08:15:22 +0000] "GET / HTTP/1.1" 200
说明 $remote_addr 已成功变成 203.208.60.1,后续 allow/deny 就可放心使用这个值。











