在Nginx的日常使用中,host和server_name是两个高频出现的概念,但它们的性质完全不同:host是HTTP请求头的一部分,由客户端(浏览器或上游代理)发送;server_name是Nginx配置文件里server块的一个指令,由服务端定义。理解两者的关系,是掌握Nginx虚拟主机、反向代理配置的基础,也是排查“请求为什么打到错误的站点”这类问题的关键。

一、从HTTP协议层面看host的本质
host是HTTP/1.1协议强制要求的请求头。当浏览器访问 http://www.ippipp.com:8080/index.html 时,发出的请求报文里会带上类似 Host: www.ippipp.com:8080 的一行。协议之所以强制这个头,是因为一台服务器上可能托管多个网站,TCP连接建立后,服务器需要知道客户端到底想访问哪个域名,才能返回正确的内容。这就是所谓的虚拟主机机制。
可以用curl直接观察这个头部:
curl -v http://127.0.0.1/ -H "Host: www.ippipp.com"
上面的命令中,IP是127.0.0.1,但通过-H参数手动指定了Host头。Nginx收到请求后,不会用IP来判断站点,而是用这个Host头的值去匹配server_name。这也解释了一个常见现象:同一台服务器,用不同域名访问会得到完全不同的页面,靠的就是Host头与server_name的配合。
需要注意,host头里可能带端口号(如www.ippipp.com:8080),Nginx匹配server_name时会自动去掉端口部分,只取域名进行比较。此外,HTTP/1.0协议不要求host头,遇到这种老协议请求,Nginx会视为host为空,走默认server的逻辑处理。
二、server_name的匹配规则与优先级
server_name写在server块中,用来声明这个虚拟主机接受哪些域名。一个典型的多站点配置如下:
server {
listen 80;
server_name www.ippipp.com; # 精确匹配,优先级最高
...
}
server {
listen 80;
server_name *.ippipp.com; # 前缀通配符
...
}
server {
listen 80;
server_name example.*; # 后缀通配符
...
}
server {
listen 80;
server_name ~^(www\.)?(.+)\.example\.com$; # 正则匹配
...
}
server {
listen 80 default_server; # 都没匹配上时走这里
server_name _;
return 444;
}
Nginx收到请求后,会拿host头的值(去掉端口)依次按以下顺序匹配:先精确匹配,再前缀通配符(最长的优先),然后后缀通配符(同样最长的优先),最后按配置文件中的出现顺序尝试正则匹配。如果全部失败,请求会交给该端口上标记为default_server的server块;如果没有显式标记,则交给配置文件中该端口的第一server块。
这里有个容易踩的坑:配置里写多个server块监听同一端口时,如果没有任何server_name能匹配请求的host,请求并不会被拒绝,而是悄悄落到默认server。很多“访问域名A却出现域名B的页面”的故障,根源就是默认server没有设置,第一个server块意外充当了兜底。生产环境建议显式声明default_server并返回444或跳转,避免恶意域名解析到你的IP时能正常打开你的站点。
三、反向代理场景下的Host改写与X-Forwarded-Host
host与server_name最容易混淆的场景是反向代理。Nginx作为代理转发请求时,默认会重新设置发往上游的host头。看下面的配置:
server {
listen 80;
server_name app.ippipp.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host; # 把客户端原始host传给上游
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
proxy_set_header Host $host;这条指令把客户端原始请求的host值原样传给后端。如果不设置这条,Nginx默认会使用proxy_pass中定义的上游名称作为host,后端应用如果依赖host做路由(比如Spring的多站点、WordPress的域名绑定),就会出现404或重定向异常。相关的变量有三个容易搞混:
$host:从请求行或host头解析出的域名,不含端口,且始终小写,即使客户端没发host头也会有值(此时为空串或请求行的值)$http_host:原始host头的完整值,可能带端口,客户端没发时为空$server_name:当前匹配到的server块中server_name指令的第一个值,是配置里的静态内容,与请求无关
举个实际例子:客户端访问 app.ippipp.com:8080,匹配到server_name为app.ippipp.com的server块。此时$host的值是app.ippipp.com,$http_host的值是app.ippipp.com:8080,而$server_name也是app.ippipp.com但来源不同——它来自配置文件而非请求。当请求被转发给上游时,如果上游还想知道客户端最初访问的域名,通常还需要传递X-Forwarded-Host头,尤其是存在多层代理的情况下,每一层都应该追加而非覆盖这个头。
四、常见故障排查思路
掌握了原理,排查问题就有章法可循。第一种典型问题:域名解析到了服务器IP,但返回的是另一个网站。排查步骤是先curl指定Host看匹配结果,再检查nginx配置中各server块的server_name拼写(多打空格、中英文混淆都是高频错误),最后确认default_server的归属。也可以用nginx -T导出完整生效配置,配合grep快速定位。
nginx -T 2>/dev/null | grep -E "server_name|listen"
第二种典型问题:后端应用拿到的host不对,导致生成的链接或重定向地址异常。这时候要检查proxy_set_header Host的设置,确认用的是$host还是$http_host,是否需要带端口。第三种问题与HTTPS有关:SNI(Server Name Indication)是TLS握手阶段客户端携带的域名信息,Nginx用它选择证书,而host头是HTTP层的字段,两者大多数时候一致,但某些客户端或攻击场景下可能不一致,因此证书校验应以SNI为准,应用层路由以host为准。
总结一下核心区别:host是请求里的动态值,server_name是配置里的静态声明;Nginx用host去匹配server_name来选择server块,匹配规则有明确的优先级顺序;反向代理转发时要显式决定如何传递host,并在上游正确读取。把这三点想清楚,虚拟主机和代理转发中的大部分疑难杂症都能迎刃而解。
Nginx配置server_namehost请求头修改时间:2026-09-12 22:52:45