deny指令必须置于http、server或limit_except块中才生效,放location内无效;宝塔IP黑名单转为if判断,性能略低;IPv6需确认Nginx支持或用正则匹配;多层防护(系统防火墙、Nginx插件、云安全组、CDN代理)可能导致“隐身拦截”。

deny指令是 Nginx 层最直接、最低开销的 IP 黑名单手段,但必须写在正确的配置块里,否则完全不生效。
为什么加了 deny 却没拦截?
常见错误是把 deny 放在 location 块里,而它只在 http、server 或 limit_except 块中有效。Nginx 的匹配逻辑是:先匹配 server,再进 location;deny 若在 location 内,会被视为访问控制指令(需配合 allow 使用),且优先级低于 location 路由本身。
- 正确位置:在网站配置文件的
server { }块顶部(location之前) - 错误位置:嵌套在
location / { }或location ~ \.php$ { }内部 - 若用宝塔「网站 → 安全 → IP黑名单」填入,实际生成的是
if ($remote_addr = xxx) { return 403; },等效但性能略低
deny 和宝塔「网站安全」里的IP黑名单有啥区别?
本质都是拒绝请求,但作用层级和灵活性不同:
-
deny 192.168.1.100;:Nginx 原生命令,支持 CIDR(如deny 203.0.113.0/24;),不依赖模块,重载即生效 - 宝塔网站安全页填的 IP:最终转为
if判断 +return 403,支持单 IP 和 CIDR,但每条都增加一次运行时判断,高并发下略拖慢 - 两者可共存,但规则重复不会叠加效果;若同时屏蔽同一 IP,以最先匹配的
server块内规则为准
屏蔽 IPv6 地址要特别注意什么?
Nginx 默认编译可能未启用 IPv6 支持,直接写 deny 2001:db8::/32; 会报错 invalid parameter "2001:db8::/32"。
- 先确认 Nginx 是否支持 IPv6:
nginx -V 2>&1 | grep -o with-http_v2_module不够,要看是否有--with-ipv6(旧版本)或更可能是默认启用(1.11.5+) - 更稳妥的方式:用
if ($remote_addr ~ "^2001:db8:") { return 403; },靠正则匹配,不依赖 IPv6 解析能力 - 宝塔「Nginx 防火墙」插件对 IPv6 的支持取决于其底层规则加载逻辑,部分版本仅解析 IPv4,填入 IPv6 段可能静默忽略
删错规则后访问仍被拒,去哪找“隐身黑名单”?
不是所有拦截都来自你刚改的配置。真实环境中常有多层并行过滤:
- 检查宝塔「安全 → 系统防火墙」:执行
iptables -L INPUT -n | grep :80或firewall-cmd --list-all,看是否有REJECT规则针对该 IP - 检查「Nginx 防火墙」插件:即使你没主动开,某些策略(如防 CC)可能自动封过 IP,记录在
/www/server/panel/plugin/nginxfirewall/logs/ - 云厂商安全组:阿里云/腾讯云控制台里的入方向规则,优先级最高,Nginx 根本收不到包
- 若用反向代理(如 CDN),真实客户端 IP 可能是
X-Forwarded-For,而$remote_addr是 CDN 节点 IP —— 此时deny失效,得用set_real_ip_from配合real_ip_header
Nginx 层的 deny 看似简单,但一旦混入多层防护、IPv6、代理头或 CIDR 计算错误,就容易变成“看起来写了,其实没拦住”。真正起效前,务必用 curl -v -x "" http://yoursite.com --interface 192.168.1.100(指定源 IP 测试)或从目标 IP 段真实机器访问验证。











