应使用 $host 或 $http_host 获取当前请求域名,因 $server_name 是静态值,始终返回 server 块中首个配置的域名;$host 优先取 host 请求头(小写、去端口),最可靠。

Nginx 本身不支持“动态生成”server_name,但可以通过变量和匹配机制准确获取当前请求实际使用的域名,关键在于:别依赖 server_name 变量取请求域名,而要用 $host 或 $http_host。
为什么不能用 $server_name 获取当前请求域名?
$server_name 是静态值——它始终返回该 server 块中 server_name 指令里写在最前面的那个名称(哪怕配置了多个),和用户实际访问的域名无关。
比如:
server {
listen 80;
server_name a.example.com b.example.com c.example.com;
return 200 "Server name: $server_name\n";
}
无论你访问 a.example.com、b.example.com 还是 c.example.com,响应里都只会输出 a.example.com。
真正能反映当前请求域名的变量
| 变量 | 行为说明 |
|---|---|
$host |
✅ 推荐首选。按优先级取: ① 请求行(如 GET / HTTP/1.1)中的 Host 部分(不含端口)② 请求头 Host 字段(自动小写、去端口)③ fallback 到匹配的 server_name(仅当前两者都缺失时) |
$http_host |
直接取请求头 Host 字段原样值(含端口,如 example.com:8080),需自行处理端口 |
$request_uri |
包含路径+查询参数,但不含域名,常配合 $host 使用 |
✅ 所以重定向、日志记录、条件判断中要获取“用户这次到底访问了哪个域名”,一律用 $host。
示例:HTTP → HTTPS 安全跳转(正确写法)
server {
listen 80;
server_name example.com www.example.com *.example.com;
return 301 https://$host$request_uri;
}
这样 a.example.com/path 就跳到 https://a.example.com/path,而不是全部跳到第一个 example.com。
进阶:从域名中提取子域或做条件路由
利用正则 server_name + 命名捕获,可把子域名存入变量供后续使用:
server {
listen 80;
server_name ~^(?<subdomain>[^.]+)\.example\.com$;
location / {
# 现在 $subdomain 就是访问者输入的子域名,如 blog → $subdomain = "blog"
proxy_pass http://backend-$subdomain;
}
}</subdomain>
注意:这种写法要求 Nginx ≥ 1.9.6,且正则必须以 ~ 开头。
兜底与调试建议
- 若某些请求没匹配到任何
server_name,Nginx 默认会交给同端口下第一个server块处理(除非显式配置default_server); - 测试时避免浏览器缓存干扰 301,用
curl -I http://xxx查看真实Location头; - 日志中记录
$host而非$server_name,便于排查多域名场景下的流量分布。
不复杂但容易忽略











