应使用 map 预处理 $host 并在 server 块顶部用 if($bad_domain) return 444,因 if 在 server 块中直接匹配 $host 不可靠,易被 ip 直连+伪造 host 绕过,且宝塔默认不支持 underscores_in_headers,导致畸形头被丢弃。

为什么直接用 if 拦截域名容易失效
宝塔面板生成的 Nginx 配置里,if 语句在 location 块外(如 server 块顶层)是允许的,但 Nginx 官方明确警告:if 在 server 上下文中行为不可靠,尤其与 $host、$http_host 等变量组合时,可能因请求头解析顺序、大小写、端口携带(如 :80)、空格或多余字符导致匹配失败。更常见的是,攻击者用 IP 直连 + 构造 Host 头绕过域名判断,而宝塔默认未开启 underscores_in_headers on,某些畸形头甚至被直接丢弃,让 if 根本收不到值。
必须用 map 预处理 $host 再配合 return
真正稳定的方式是把域名匹配逻辑提前到 http 块(全局生效),用 map 构建一个标记变量,再在 server 块中统一拦截。宝塔虽不直接提供 http 块编辑入口,但支持「配置修改 → 网站 → 配置文件」底部追加全局配置,或通过「宝塔终端」手动编辑 /www/server/nginx/conf/nginx.conf(改完需重载 nginx -s reload)。
-
map必须定义在http块内,不能放在server或location中 - 匹配目标应同时覆盖带端口和不带端口的
Host值,例如evil.com和evil.com:443 - 使用正则时注意转义点号:
~* ^evil\.com(:[0-9]+)?$,~*表示忽略大小写 - 最终用
return 444(Nginx 特有,直接断连,比403更隐蔽)或return 404
示例(加在 http 块末尾):
map $host $bad_domain {
default 0;
~* ^evil\.com(:[0-9]+)?$ 1;
~* ^malware\-site\.org$ 1;
}
然后在每个要保护的网站的 server 块顶部加入:
if ($bad_domain) {
return 444;
}
宝塔界面操作时的三个关键避坑点
在「网站 → 设置 → 配置文件」里直接改,容易踩这三个坑:
- 误把
map写进server块 → Nginx 启动报错:"map" directive is not allowed here - 用
$http_host替代$host→$http_host可能为空或含非法字符,$host是 Nginx 解析后的标准化域名,更可靠 - 规则加在
location /里 →if在location中仅对当前路径生效,静态资源、API 接口等子路径可能逃逸
正确做法:所有拦截逻辑只放在 server 块最上方(root、location 之前),且依赖全局 map 变量。
如何验证规则是否生效且不误杀
别只靠浏览器访问测试 —— 浏览器会自动补全 http:// 并可能缓存 DNS 或 301,干扰判断。用 curl 手动构造 Host 头最准:
- 正常域名(应放行):
curl -H "Host: your-site.com" http://your-server-ip/ - 恶意域名(应断连):
curl -I -H "Host: evil.com" http://your-server-ip/→ 应无响应或返回空 - 大小写混合(应拦截):
curl -H "Host: EvIl.CoM" http://your-server-ip/→map的~*能覆盖
查看 Nginx 错误日志确认:tail -f /www/wwwlogs/nginx_error.log,若看到 rewrite or internal redirection cycle,说明 if 位置错误或变量引用异常;若完全无日志,则规则未触发,优先检查 map 是否加载、$host 实际值(可临时加 log_format 打印)。
实际部署时,map 的键值映射和 if 的执行顺序是唯一可靠的组合,其它任何把逻辑塞进 location 或依赖多层 if 嵌套的做法,在高并发或畸形请求下都可能漏掉恶意流量。











