nginx 中 proxy_pass 本身不支持 ip 黑名单,需结合 deny/allow 或 geo+map 在 server/location 块中前置拦截;务必通过 real_ip_header 等配置获取真实客户端 ip,否则规则失效。

在 Nginx 中,proxy_pass 本身不直接提供 IP 黑名单功能,但可以通过结合 deny 指令(配合 allow)或更灵活的 geo + map 方式,在请求到达 upstream 之前就拦截黑名单 IP。关键是要在 proxy_pass 执行前完成判断 —— 通常放在 server 或 location 块中。
使用 deny/allow 简单黑名单(适用于固定 IP 或小规模列表)
这是最直接的方式,适合少量明确要封禁的客户端 IP(注意:这里指真实客户端 IP,不是代理 IP)。
- 确保 Nginx 能获取真实客户端 IP:如果前端有反向代理(如 CDN、LB),需配置
real_ip_header和set_real_ip_from,否则$remote_addr是上一跳地址,不是用户真实 IP - 在
location或server块中写入拦截规则:
location /api/ {
# 允许所有,再逐条拒绝(顺序重要)
allow all;
deny 192.168.10.5;
deny 203.0.113.42;
deny 2001:db8::1;
proxy_pass http://backend;
}
⚠️ 注意:deny/allow 是按顺序匹配,第一条匹配即生效;allow all 必须放在 deny 前,否则全被拒绝。
用 geo + map 实现可维护的黑名单(推荐用于中等规模)
当黑名单 IP 较多或需动态管理时,硬编码 deny 不易维护。可用 geo 定义黑名单标识,再用 map 转为返回状态,最后用 return 拦截。
- 在
http块顶部定义黑名单 IP 映射:
geo $bad_client {
default 0;
192.168.10.5 1;
203.0.113.42 1;
192.168.10.0/24 1;
::1 1;
}
- 再用
map将标记转为 HTTP 状态码(如 403):
map $bad_client $blocked {
1 403;
default 0;
}
- 在
location中检查并拦截:
location /api/ {
if ($blocked) { return $blocked; }
proxy_pass http://backend;
}
✅ 优势:支持 CIDR、IPv6、集中管理;geo 查找高效;if 在此场景下是安全的(仅用于变量判断)。
处理真实客户端 IP 的前提:正确设置 real_ip
如果请求经过 CDN、云负载均衡或另一层 Nginx,$remote_addr 是它们的出口 IP,不是用户真实 IP。必须还原:
- 确认上游代理在请求头中传了真实 IP(通常是
X-Forwarded-For) - 在
http或server块中配置信任源和提取逻辑:
set_real_ip_from 10.0.0.0/8; # 你自己的可信代理网段
set_real_ip_from 2001:db8::/32;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
配置后,$remote_addr 和 geo 指令才真正基于用户原始 IP 匹配。
进阶:运行时热更新黑名单(无需 reload)
若黑名单频繁变动,可将 IP 列表放在外部文件(如 /etc/nginx/conf.d/blacklist.conf),内容为多个 deny xxx; 行,然后在 location 中 include blacklist.conf;。更新后执行 nginx -t && nginx -s reload 即可生效 —— 这比改主配置更清晰。
更轻量方案:用 Lua(OpenResty)或 NJS 模块实现内存级黑名单 + API 动态增删,但需额外模块支持。











