Nginx中 $host $http_host $proxy_host的区别
| 变量 | 值来源 | 是否显示端口 | 说明 |
|---|---|---|---|
| $host | 按照 请求行中的Host > 请求头中的Host > server_name 的值 优先级获取 | 否 | "Host:port"显示值为a:b的时候,只显示a |
| $http_host | 请求头中的Host | 是 | "Host:port",port存在就显示,请求头中Host是多少 $http_host就是多少 |
| $proxy_host | proxy_pass 指定的地址 | 是(默认80不显示) | "Host:port" proxy_pass指定的 "Host:port",当port为80的时候只有“Host”,其它端口,正常显示为 “Host:port” |
官方文档说明
$host 按以下优先顺序排列:请求行中的主机名,或 “Host ”请求头字段中的主机名,或与请求匹配的服务器名(server_name)
$http_ name 任意请求标头字段;变量名的最后一部分是转换为小写的字段名,破折号由下划线代替
如:
请求中有
Host: example.com
User-Agent: curl/7.68.0
X-Forwarded-For: 1.2.3.4就可以使用
$http_host
$http_user_agent
$http_x_forwarded_forngx_http_proxy_module 模块变量
$proxy_host : proxy_pass指令中指定的代理服务器的名称和端口 ;
测试携带端口和不携带端口的变量值
通过Nginx日志,查看这三个变量的值
log_format hosttest '$host|$http_host|$proxy_host';
server {
listen 8888 default_server;
listen 80;
server_name www.example.com;
charset utf-8;
location /test {
proxy_pass http://192.168.100.100:9200;
access_log /var/log/nginx/test.log hosttest;
}
location /80 {
proxy_pass http://192.168.100.100;
access_log /var/log/nginx/test.log hosttest;
}
access_log /var/log/nginx/static_access.log main;
error_log /var/log/nginx/static_error.log;
}宿主机,在 /etc/hosts文件添加Hosts解析
192.168.1.236 www.example.com通过curl测试
测试请求
curl www.example.com:8888/80
curl www.example.com:8888/test
curl www.example.com/80
curl www.example.com/test
curl www.example.com:80/80
curl www.example.com:80/test对应日志
www.example.com|www.example.com:8888|192.168.100.100
www.example.com|www.example.com:8888|192.168.100.100:9200
www.example.com|www.example.com|192.168.100.100
www.example.com|www.example.com|192.168.100.100:9200
www.example.com|www.example.com|192.168.100.100
www.example.com|www.example.com|192.168.100.100:9200结果
| 请求地址 | 请求地址携带端口 | proxy_pass | $host | $http_host | $proxy_host |
|---|---|---|---|---|---|
| www.example.com:8888/80 | 是 | http://192.168.100.100 | www.example.com | www.example.com:8888 | 192.168.100.100 |
| www.example.com:8888/test | 是 | http://192.168.100.100:9200 | www.example.com | www.example.com:8888 | 192.168.100.100:9200 |
| www.example.com/80 | 否 | http://192.168.100.100 | www.example.com | www.example.com | 192.168.100.100 |
| www.example.com/test | 否 | http://192.168.100.100:9200 | www.example.com | www.example.com | 192.168.100.100:9200 |
| www.example.com:80/80 | 是 | http://192.168.100.100:80 | www.example.com | www.example.com | 192.168.100.100 |
| www.example.com:80/test | 是 | http://192.168.100.100:9200 | www.example.com | www.example.com | 192.168.100.100:9200 |
1.$host 与 请求行中的Host、请求头中的Host、匹配的server_name的值有关,但是在这里体现的不明显。
因为通过curl请求,请求行中不携带Host,自动在请求头中添加Host,且会自动将www.example.com:80中的端口删除,使请求头中的Host为www.example.com,不过请求头中携带80端口,$host也不会携带端口,比如:
$ curl www.example.com:80/80 -v -H "Host: www.example.com:80"
* Trying 192.168.1.236...
* TCP_NODELAY set
* Connected to www.example.com (192.168.1.236) port 80 (#0)
> GET /80 HTTP/1.1
> Host: www.example.com:80
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Date: Mon, 12 May 2025 09:29:23 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 150
< Connection: keep-alive
< Server: SwiftWaf
<
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>openresty</center>
</body>
</html>
* Connection #0 to host www.example.com left intact日志
www.example.com|www.example.com:80|192.168.100.1002.$http_host 请求头中的Host是多少,这里就是多少。
同样的原因,在请求curl www.example.com:80/80的时候,请求头中的Host为www.example.com,curl自动将端口删除了,这里表示的不够明显,如果请求头中的是www.example.com:80,那么$http_host的值就是www.example.com:80。
3.$proxy_host 与 $proxy_pass有关,转发非80端口,为Host:Port,转发80端口,不管携带不携带端口,都是Host
注意:
通过curl命令测试,请求行中都不包含Host,因为请求行是GET /80 HTTP/1.1,而不是GET http://www.example.com:8888/80 HTTP/1.1
$ curl www.example.com:8888/80 -v
* Trying 192.168.1.236...
* TCP_NODELAY set
* Connected to www.example.com (192.168.1.236) port 8888 (#0)
> GET /80 HTTP/1.1
> Host: www.example.com:8888
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Date: Mon, 12 May 2025 09:19:14 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 150
< Connection: keep-alive
< Server: SwiftWaf
<
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>openresty</center>
</body>
</html>
* Connection #0 to host www.example.com left intact可以通过nc模拟请求行中携带Host的请求,比如
# 请求行携带Host,请求头不携带Host
printf "GET http://www.example.com:8888/80 HTTP/1.0\r\n\r\n" | nc 192.168.1.236 80
# 请求行携带Host,请求头携带host
printf "GET http://www.example.com:8888/80 HTTP/1.0\r\nHost: foo.com\r\n\r\n" | nc 192.168.1.236 80通过nc测试
# 请求行携带Host,请求头不携带Host
printf "GET http://www.example.com:8888/80 HTTP/1.0\r\n\r\n" | nc 192.168.1.236 80
# 请求行携带Host,请求头携带host
printf "GET http://www.example.com:8888/80 HTTP/1.0\r\nHost: foo.com\r\n\r\n" | nc 192.168.1.236 80
# 请求行不携带Host,请求头不携带Host
printf "GET /80 HTTP/1.0\r\n\r\n" | nc 192.168.1.236 80Nginx配置
log_format hosttest '$host|$http_host|$proxy_host';
server {
listen 8888 default_server;
listen 80;
server_name www.example.com;
charset utf-8;
location /test1 {
proxy_pass http://192.168.100.100:8080;
access_log /var/log/nginx/test.log hosttest;
}
location /test2 {
proxy_pass http://192.168.100.100;
access_log /var/log/nginx/test.log hosttest;
}
location /test3 {
proxy_pass http://192.168.100.100:80;
access_log /var/log/nginx/test.log hosttest;
}
access_log /var/log/nginx/static_access.log main;
error_log /var/log/nginx/static_error.log;
}测试命令
# 请求行携带Host 不同,请求头不携带Host,接口相同
printf "GET http://www.example.com:8888/test1 HTTP/1.0\r\n\r\n" | nc 192.168.1.236 8888
printf "GET http://www.example.com/test1 HTTP/1.0\r\n\r\n" | nc 192.168.1.236 8888
printf "GET http://www.example.com:80/test1 HTTP/1.0\r\n\r\n" | nc 192.168.1.236 8888
# 请求行不携带Host,请求头携带host 不同,接口相同
printf "GET /test1 HTTP/1.0\r\nHost: www.example.com:8888\r\n\r\n" | nc 192.168.1.236 8888
printf "GET /test1 HTTP/1.0\r\nHost: www.example.com\r\n\r\n" | nc 192.168.1.236 8888
printf "GET /test1 HTTP/1.0\r\nHost: www.example.com:80\r\n\r\n" | nc 192.168.1.236 8888
# 请求行不携带Host,请求头不携带Host,接口不同
printf "GET /test1 HTTP/1.0\r\n\r\n" | nc 192.168.1.236 8888
printf "GET /test2 HTTP/1.0\r\n\r\n" | nc 192.168.1.236 8888
printf "GET /test3 HTTP/1.0\r\n\r\n" | nc 192.168.1.236 8888
日志
www.example.com|-|192.168.100.100:8080
www.example.com|-|192.168.100.100:8080
www.example.com|-|192.168.100.100:8080
www.example.com|www.example.com:8888|192.168.100.100:8080
www.example.com|www.example.com|192.168.100.100:8080
www.example.com|www.example.com:80|192.168.100.100:8080
www.example.com|-|192.168.100.100:8080
www.example.com|-|192.168.100.100
www.example.com|-|192.168.100.100| 请求 | 请求行中Host | 请求头中Host | proxy_pass | $host | $http_host | $proxy_host |
|---|---|---|---|---|---|---|
| GET http://www.example.com:8888/test1 HTTP/1.0\r\n\r\n | www.example.com:8888 | 无 | http://192.168.100.100:8080 | www.example.com (请求行中的Host) | - | 192.168.100.100:8080 |
| GET http://www.example.com/test1 HTTP/1.0\r\n\r\n | www.example.com | 无 | http://192.168.100.100:8080 | www.example.com (请求行中的Host) | - | 192.168.100.100:8080 |
| GET http://www.example.com:80/test1 HTTP/1.0\r\n\r\n | www.example.com:80 | 无 | http://192.168.100.100:8080 | www.example.com (请求行中的Host) | - | 192.168.100.100:8080 |
| GET /test1 HTTP/1.0\r\nHost: www.example.com:8888\r\n\r\n | 无 | www.example.com:8888 | http://192.168.100.100:8080 | www.example.com (请求头中的Host) | www.example.com:8888 | 192.168.100.100:8080 |
| GET /test1 HTTP/1.0\r\nHost: www.example.com\r\n\r\n | 无 | www.example.com | http://192.168.100.100:8080 | www.example.com (请求头中的Host) | www.example.com | 192.168.100.100:8080 |
| GET /test1 HTTP/1.0\r\nHost: www.example.com:80\r\n\r\n | 无 | www.example.com:80 | http://192.168.100.100:8080 | www.example.com (请求头中的Host) | www.example.com:80 | 192.168.100.100:8080 |
| GET /test1 HTTP/1.0\r\n\r\n | 无 | 无 | http://192.168.100.100:8080 | www.example.com (server_name) | - | 192.168.100.100:8080 |
| GET /test2 HTTP/1.0\r\n\r\n | 无 | 无 | http://192.168.100.100 | www.example.com (server_name) | - | 192.168.100.100 |
| GET /test3 HTTP/1.0\r\n\r\n | 无 | 无 | http://192.168.100.100:80 | www.example.com (server_name) | - | 192.168.100.100 (携带80不显示) |
测试$host中的优先级
进一步测试 $host,验证是否是官方说的顺序:
请求行中的主机名 > “Host ”请求头字段中的主机名 > 与请求匹配的服务器名(server_name)
设置一个调试配置
server {
listen 80;
server_name _;
location /test {
return 200 "$host\n";
}
}测试场景1:手动构造 Request Line 带 Host(非常规,需工具支持)
printf "GET http://foo.com/test HTTP/1.1\r\nHost: bar.com\r\n\r\n" | nc 192.168.1.236 80返回的$host是foo.com,说明Nginx优先使用请求行中的Host
通过curl -v 'http://www.example.com/test' -H "Host: foo.com"测试失败
curl -v 'http://www.example.com/test' -H "Host: foo.com"
* Trying 192.168.1.236...
* TCP_NODELAY set
* Connected to www.example.com (192.168.1.236) port 80 (#0)
> GET /test HTTP/1.1
> Host: foo.com
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 12 May 2025 08:54:41 GMT
< Content-Type: application/octet-stream
< Content-Length: 8
< Connection: keep-alive
< Server: SwiftWaf
<
foo.com
* Connection #0 to host www.example.com left intact原因是:
curl -v 'http://www.example.com/test' 会解析域名,填入 Host 头,但请求行是:
GET /test HTTP/1.1而不是
GET http://www.example.com/test HTTP/1.1所以你的 $host 来源仍然是 Host 头字段,不是 request line 中的 host。
真正测试 “请求行中的 host 优先级” 的方法:
你需要手工伪造带绝对 URI 的 request line,例如使用 nc:
printf "GET http://www.example.com/test HTTP/1.1\r\nHost: foo.com\r\n\r\n" | nc 192.168.1.236 80测试结果
$ printf "GET http://www.example.com/test HTTP/1.1\r\nHost: foo.com\r\n\r\n" | nc 192.168.1.236 80
HTTP/1.1 200 OK
Date: Mon, 12 May 2025 08:56:40 GMT
Content-Type: application/octet-stream
Content-Length: 16
Connection: keep-alive
Server: SwiftWaf
www.example.com
^C可以看到$host返回www.example.com,证明 Nginx 确实优先使用了请求行中的 host。
测试场景2:请求行不添加host,请求头添加 Host
curl 会自动添加Host,实际请求行是GET /test HTTP/1.1,不包含Host,请求头中包含HostHost: www.example.com。
最终$host返回请求头中的Hostwww.example.com,说明 请求头中的Host 优先级高于匹配的server_name值。
$ curl -v http://www.example.com/test
* Trying 192.168.1.236...
* TCP_NODELAY set
* Connected to www.example.com (192.168.1.236) port 80 (#0)
> GET /test HTTP/1.1
> Host: www.example.com
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 12 May 2025 09:06:53 GMT
< Content-Type: application/octet-stream
< Content-Length: 16
< Connection: keep-alive
< Server: SwiftWaf
<
www.example.com
* Connection #0 to host www.example.com left intact通过nc模拟 请求行不添加host,请求头添加Host的场景
printf "GET /test HTTP/1.1\r\nHost: foo.com\r\n\r\n" | nc 192.168.1.236 80结果
$ printf "GET /test HTTP/1.1\r\nHost: foo.com\r\n\r\n" | nc 192.168.1.236 80
HTTP/1.1 200 OK
Date: Mon, 12 May 2025 09:04:48 GMT
Content-Type: application/octet-stream
Content-Length: 8
Connection: keep-alive
Server: SwiftWaf
foo.com
请求行中不包含Host,请求头包含Host,$host返回请求头中包含的Hostfoo.com,没有返回server_name中的值,说明请求头中的Host优先级高于server_name的名称。
测试场景3:完全不传 Host 头(curl 会自动加)
用 nc(netcat)构造请求来模拟无 Host 请求:
printf "GET /test HTTP/1.0\r\n\r\n" | nc 192.168.1.236 80测试结果
$ printf "GET /test HTTP/1.0\r\n\r\n" | nc 192.168.1.236 80
HTTP/1.1 200 OK
Date: Mon, 12 May 2025 08:58:31 GMT
Content-Type: application/octet-stream
Content-Length: 2
Connection: close
Server: SwiftWaf
_通过nc模拟请求行中没有host,请求头中不包含Host的请求,结果 $host返回server_name的值 _。说明server_name优先级最低
如果server_name为www.example.com,则$host返回www.example.com,比如:
server {
listen 80 default_server;
server_name www.example.com;
location /test {
return 200 "$host\n";
}
}测试
$ printf "GET /test HTTP/1.0\r\n\r\n" | nc 192.168.1.236 80
HTTP/1.1 200 OK
Date: Mon, 12 May 2025 09:02:44 GMT
Content-Type: application/octet-stream
Content-Length: 16
Connection: close
Server: SwiftWaf
www.example.com注意:
如果使用 HTTP/1.1 协议发起不包含Host的请求,Nginx会直接返回 400 Bad Request(这是符合 HTTP/1.1 标准的 —— 必须带 Host)。
HTTP/1.0 没有这个强制要求,因此请求成功。
$ printf "GET /test HTTP/1.1\r\n\r\n" | nc 192.168.1.236 80
HTTP/1.1 400 Bad Request
Date: Mon, 12 May 2025 08:58:36 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 154
Connection: close
Server: SwiftWaf
<html>
<head><title>400 Bad Request</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<hr><center>openresty</center>
</body>
</html>