变量值来源是否显示端口说明
$host按照 请求行中的Host > 请求头中的Host > server_name 的值 优先级获取"Host:port"显示值为a:b的时候,只显示a
$http_host请求头中的Host"Host:port",port存在就显示,请求头中Host是多少 $http_host就是多少
$proxy_hostproxy_pass 指定的地址是(默认80不显示)"Host:port" proxy_pass指定的 "Host:port",当port为80的时候只有“Host”,其它端口,正常显示为 “Host:port”

官方文档说明

ngx_http_core_module 模块变量

$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_for

ngx_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/80http://192.168.100.100www.example.comwww.example.com:8888192.168.100.100
www.example.com:8888/testhttp://192.168.100.100:9200www.example.comwww.example.com:8888192.168.100.100:9200
www.example.com/80http://192.168.100.100www.example.comwww.example.com192.168.100.100
www.example.com/testhttp://192.168.100.100:9200www.example.comwww.example.com192.168.100.100:9200
www.example.com:80/80http://192.168.100.100:80www.example.comwww.example.com192.168.100.100
www.example.com:80/testhttp://192.168.100.100:9200www.example.comwww.example.com192.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.100

2.$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 80

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 /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请求头中Hostproxy_pass$host$http_host$proxy_host
GET http://www.example.com:8888/test1 HTTP/1.0\r\n\r\nwww.example.com:8888http://192.168.100.100:8080www.example.com (请求行中的Host)-192.168.100.100:8080
GET http://www.example.com/test1 HTTP/1.0\r\n\r\nwww.example.comhttp://192.168.100.100:8080www.example.com (请求行中的Host)-192.168.100.100:8080
GET http://www.example.com:80/test1 HTTP/1.0\r\n\r\nwww.example.com:80http://192.168.100.100:8080www.example.com (请求行中的Host)-192.168.100.100:8080
GET /test1 HTTP/1.0\r\nHost: www.example.com:8888\r\n\r\nwww.example.com:8888http://192.168.100.100:8080www.example.com (请求头中的Host)www.example.com:8888192.168.100.100:8080
GET /test1 HTTP/1.0\r\nHost: www.example.com\r\n\r\nwww.example.comhttp://192.168.100.100:8080www.example.com (请求头中的Host)www.example.com192.168.100.100:8080
GET /test1 HTTP/1.0\r\nHost: www.example.com:80\r\n\r\nwww.example.com:80http://192.168.100.100:8080www.example.com (请求头中的Host)www.example.com:80192.168.100.100:8080
GET /test1 HTTP/1.0\r\n\r\nhttp://192.168.100.100:8080www.example.com (server_name)-192.168.100.100:8080
GET /test2 HTTP/1.0\r\n\r\nhttp://192.168.100.100www.example.com (server_name)-192.168.100.100
GET /test3 HTTP/1.0\r\n\r\nhttp://192.168.100.100:80www.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

返回的$hostfoo.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_namewww.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>

标签: Nginx

添加新评论