Nginx 不支持 server_name 中直接使用完整 PCRE 正则匹配任意复杂域名,但可通过 ~ 开头的正则(区分大小写)或 ~*(不区分大小写)精确匹配 Host 字符串,配合 map 模块实现子域名提取、黑白名单等灵活路由,并用 if 在 server 级做简单 Host 判断与跳转。

Nginx 本身不支持在 server_name 指令中直接使用 PCRE 风格的正则表达式来匹配「任意复杂域名结构」,但它提供了几种实用且可靠的机制来实现类似效果——关键在于理解其匹配逻辑、合理组合使用通配符、正则语法和变量判断。
server_name 支持的正则语法有限但够用
Nginx 的 server_name 允许以 ~ 开头的正则表达式(区分大小写)或 ~*(不区分大小写),但仅用于**精确匹配 Host 请求头**,且有重要限制:
- 不能跨子域名层级做“模糊中间匹配”,比如
~* ^[a-z]+\.example\.com$可匹配api.example.com、blog.example.com,但无法匹配dev.api.example.com(除非显式写出) - 正则只作用于整个 Host 字符串,不解析 DNS 层级;
www.example.com和example.com是两个不同字符串 - 多个
server_name条目按「精确匹配 > 最长通配符 > 正则顺序(从上到下)」优先级生效,正则不是最高优先级
用 map 指令预处理 Host 实现灵活路由
真正处理“复杂规则”(如多级子域提取、黑白名单、动态重定向)应配合 map 模块,它支持完整 PCRE 并可赋值给变量,在后续逻辑中复用:
map $host $tenant_id {
~* ^([a-z0-9]+)\.app\.example\.com$ $1;
~* ^([a-z0-9]+)-staging\.app\.example\.com$ "$1-staging";
default "";
}
然后在 server 块中使用:
server {
listen 80;
server_name app.example.com ~* \.app\.example\.com$;
<pre class="brush:php;toolbar:false;">if ($tenant_id = "") {
return 404;
}
location / {
proxy_set_header X-Tenant-ID $tenant_id;
proxy_pass https://backend;
}}
这样就能安全提取子域名、过滤非法 Host、甚至联动 ACL 控制。
结合 if + $host 实现条件跳转或拦截
虽然 Nginx 官方不推荐在 location 中大量使用 if,但在 server 级别对 Host 做简单判断是常见且稳定的用法:
- 拒绝非预期域名:
if ($host !~ ^(www|api|admin)\.example\.com$) { return 444; } - 自动补 www:
if ($host = 'example.com') { return 301 https://www.example.com$request_uri; } - 匹配泛型测试环境:
if ($host ~* \.(dev|test|staging)\.example\.com$) { set $env "dev"; }
注意:if 中不能嵌套,且只支持有限指令(return、rewrite、set、break),避免用于复杂逻辑。
真实场景建议:分层设计 + 日志验证
复杂 Virtual Host 规则容易出错,推荐实践:
- 把「必须匹配的主域名」放在
server_name的精确/通配项里(如example.com www.example.com *.api.example.com) - 把「需要提取、分类、拦截的逻辑」下沉到
map或if,并用log_format记录 $host 和匹配变量,方便调试 - 避免在正则中使用过于宽泛的模式(如
~* .*),防止意外覆盖其他 server 块 - 用
nginx -t测试配置后,再用curl -H "Host: xxx" http://127.0.0.1手动验证各分支
不复杂但容易忽略细节。关键是把「匹配」和「路由决策」解耦,让 Nginx 各模块各司其职。











