Nginx中推荐用map指令提取子域名,需在http块中定义正则映射$host到$subdomain等变量;server_name仍需正则匹配以启用泛域名支持,但变量提取由map完成,安全高效。

在 Nginx 中,可以通过 server_name 指令配合正则表达式捕获子域名,并将捕获内容赋值给自定义变量(如 $subdomain),但要注意:Nginx **原生不支持直接在 server_name 正则中用 set 赋值**,必须借助 map 指令或 if + set(需谨慎)来实现。
使用 map 指令提取子域名(推荐)
map 是最安全、高效且官方推荐的方式,它基于 server_name 匹配后的 $host(或 $http_host)进行正则匹配并映射变量。
- 在
http块中定义映射关系,例如提取一级子域名(如api.example.com→api):
http {
map $host $subdomain {
~^([a-zA-Z0-9\-]+)\.example\.com$ $1;
default "";
}
# 其他配置...
}- 在
server块中直接使用$subdomain变量,比如记录日志、转发请求或条件判断:
server {
server_name ~^([a-zA-Z0-9\-]+)\.example\.com$ example.com;
<pre class='brush:php;toolbar:false;'>location / {
add_header X-Subdomain $subdomain;
proxy_pass https://backend-$subdomain;
}}
注意:server_name 仍需用正则匹配以启用泛域名支持(否则 $host 不会进入该虚拟主机),但变量提取逻辑完全由 map 承担。
用 if + set 提取(不推荐用于关键逻辑)
虽然可行,但 if 在 location 外部行为受限,且嵌套或与某些指令(如 proxy_pass)混用易出错,仅适合简单判断场景:
server {
server_name ~^([a-zA-Z0-9\-]+)\.example\.com$;
<pre class='brush:php;toolbar:false;'>set $subdomain "";
if ($host ~ ^([a-zA-Z0-9\-]+)\.example\.com$) {
set $subdomain $1;
}
location / {
add_header X-Debug-Sub $subdomain;
}}
⚠️ 风险提示:Nginx 官方文档明确指出 if 在 server 级别可能引发意外行为;变量作用域和执行时机也不如 map 稳定。
匹配多级子域名(如 a.b.example.com)
只需调整正则即可。例如提取最左侧子域名(a)或完整前缀(a.b):
- 提取第一级(
a):~^([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-]+)\.example\.com$→ 用$1 - 提取全部前缀(
a.b):~^([a-zA-Z0-9\-\.]+)\.example\.com$→ 注意转义点号,用$1
示例(map 版):
map $host $full_subdomain {
~^([a-zA-Z0-9\-\.]+)\.example\.com$ $1;
default "";
}验证是否生效
重启 Nginx 后,可通过以下方式快速验证:
- 用
curl -H "Host: api.example.com" http://127.0.0.1/发起请求 - 检查响应头是否包含
X-Subdomain: api - 或在日志中添加
log_format记录$subdomain,再查 access.log
不复杂但容易忽略的是:确保 server_name 正则能真正匹配请求的 Host,且 map 必须定义在 http 块中——写在 server 内会报错。


















