nginx的map指令不能直接匹配server_name,但可通过$host或$http_host变量实现基于域名的动态多租户路由分发,常用于生产环境。

nginx 的 map 指令本身不能直接匹配 server_name,但可以配合 $host 或 $http_host 变量,实现基于域名的动态多租户路由分发——这是生产中常见且稳定的方案。
理解 map 与 server_name 的分工
server_name 是在 SSL 握手后、HTTP 请求解析阶段才生效的“静态匹配项”,用于选择哪个 server 块处理请求;而 map 是运行时变量映射机制,作用于请求处理阶段。因此不能用 map 直接“重写 server_name”,但可以:
- 用 $host(客户端发送的 Host 头)或 $http_host 作为 map 的输入源
- 将不同租户域名映射为内部标识(如 tenant_id、backend_group、root_path)
- 在 location 或 proxy_pass 中引用该映射结果做动态路由
基础 map + 域名路由示例
以下配置将 sub1.example.com、sub2.example.com 映射到不同后端集群,并自动设置静态资源路径:
map $host $tenant_backend {
default "backend-default";
~^([a-z0-9]+)\.example\.com$ "$1";
}
map $host $tenant_root {
default "/var/www/default";
~^([a-z0-9]+)\.example\.com$ "/var/www/tenants/$1";
}
server {
listen 80;
server_name ~^(?<subdomain>[a-z0-9]+)\.example\.com$ example.com;
root $tenant_root;
location /api/ {
proxy_pass http://$tenant_backend;
proxy_set_header Host $host;
proxy_set_header X-Tenant-ID $tenant_backend;
}
location / {
try_files $uri $uri/ =404;
}
}
</subdomain>支持通配符与正则高级匹配
map 支持 ~(区分大小写)、~*(不区分)、=(精确)等匹配方式,适合多级租户结构:
- 匹配 multi.tenant.example.com 和 tenant.example.com → 统一归为 tenant
- 用 ~*^([^.]+)\.example\.com$ 匹配一级子域(忽略 www、api 等保留名)
- 结合 geo 或 map 嵌套(通过变量链式赋值)实现灰度:$tenant_env = “prod” / “staging”
注意:正则捕获组(如 $1)在 map 中可直接使用,但需确保被映射变量已定义且非空;建议加 default 分支防 fallback 失败。
安全与运维要点
动态路由易引入安全隐患,需主动控制:
- 禁止用户可控输入(如 Host 头)直接拼接路径 —— 使用白名单 map 或预定义值集
- 租户隔离:用不同的 upstream 组、proxy_buffering、client_max_body_size 避免相互干扰
- 日志中记录 $tenant_backend 或 $host,便于排查租户流量异常
- reload nginx 前用 nginx -t 验证 map 语法;map 不支持嵌套 if,逻辑复杂时拆成多个 map 变量











