nginx可通过map指令动态解析子域名并映射到对应目录:先提取子域名,再反转点分结构为路径,最后拼接基础根路径实现自动root定位,全过程无需rewrite或proxy_pass。

Nginx 的 root 指令本身不支持动态解析子域名,但可以通过 map 指令配合 server_name 提取子域名片段,再构造出对应物理路径,最终由 root 引用该动态路径实现自动映射。整个过程不依赖 rewrite 或 proxy_pass,而是靠变量传递和路径拼接完成。
核心逻辑是:把子域名(如 api.v2.dev)按点分隔、反转顺序,转成目录层级(如 /dev/v2/api),再拼到基础根路径后
提取并标准化子域名
在 http 块中定义 map,从 host 中剥离主域名,保留子域名部分,并统一格式:
map $host $subdomain_path {
~^(?<s>.+)\.example\.com$ $s;
default "";
}</s>
这个规则会把 api.v2.dev.example.com 提取为 api.v2.dev,把 blog.example.com 提取为 blog。
将子域名转为路径结构
继续用 map 把 api.v2.dev → /dev/v2/api(点变斜杠 + 反转):
map $subdomain_path $docroot_path {
"~^([^.]*)$" /$1;
"~^([^.]*)\.([^.]*)$" /$2/$1;
"~^([^.]*)\.([^.]*)\.([^.]*)$" /$3/$2/$1;
# 可按需扩展更多层级,如四段、五段子域名
default "";
}
注意:Nginx 的
map不支持循环或递归,多级子域名需显式写出各长度分支。实际部署中常见 2–4 级,够用。
组合成完整 root 路径
再套一层 map,拼上基础存储目录:
map $docroot_path $dynamic_root {
"~^/(.*)$" /var/www/example/$1;
default /var/www/example;
}
这样 api.v2.dev.example.com 最终对应 /var/www/example/dev/v2/api,文件结构与子域名语义对齐。
在 server 块中使用动态 root
server {
listen 80;
server_name ~^(.+)\.example\.com$;
root $dynamic_root;
location / {
index index.html;
try_files $uri $uri/ =404;
}
# 静态资源可加缓存
location ~ \.(js|css|png|jpg|gif|ico)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
-
server_name ~^(.+)\.example\.com$启用正则匹配,捕获任意子域名; -
root $dynamic_root直接引用前面 map 构造的变量; - 所有请求都基于该动态路径查找文件,无需每个子域名单独写一个 server 块。
实际目录结构示例
假设你有这些子域名:
-
blog.example.com→/var/www/example/blog -
admin.test.staging.example.com→/var/www/example/staging/test/admin -
v2.api.example.com→/var/www/example/api/v2
对应服务器上需真实存在这些目录,且包含 index.html 或其他可服务内容。
注意事项
- 确保
nginx -t能通过,map必须定义在http块内,不能放在server或location里; - 子域名提取正则要严格匹配主域名(如
.example.com),避免误匹配其他域名; -
$dynamic_root是字符串拼接结果,Nginx 不会自动创建目录,需手动建立并设好权限; - 若子域名含下划线
_,注意 DNS 和 Nginx 默认不支持下划线在 host 中,建议只用字母、数字、短横线; - 日志中可通过
$dynamic_root变量记录实际服务路径,便于排查 404:
log_format debug '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'root="$dynamic_root"';
不复杂但容易忽略











