背景知识

HTTP 请求由是请求行、请求头和请求体组成,其中请求行的格式如下:

  1. Request-Line = Method SP Request-URI SP HTTP-Version CRLF

其中 Request-URI 的格式如下:

  1. Request-URI = "*" | absoluteURI | abs_path | authority

absoluteURI 格式的请求行中包含 host 信息

  1. GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1

而 abs_path 格式的请求行,它将 absoluteURI 进行了拆分,请求行只保留资源路径,而 host 被放在请求头,这是 HTTP/1.1 协议所规定的格式。

  1. GET /pub/WWW/TheProject.html HTTP/1.1
  2. Host: www.w3.org

三者的含义

有了以上背景知识,就比较容易区分 3 者的关系和区别了。

host

host 变量的值会按以下优先级获取:

  1. 请求行中的 host
  2. 请求头中的 Host 头部
  3. 与请求匹配的 server name

官方是这样描述 (ref):

$host in this order of precedence: host name from the request line, or host name from the “Host” request header field, or the server name matching a request.

Nginx 获取 host 的过程与 HTTP 协议所规定 host 的获取规则是一致的 (ref):

An origin server that does differentiate resources based on the host requested (sometimes referred to as virtual hosts or vanity host names) MUST use the following rules for determining the requested resource on an HTTP/1.1 request:

  1. If Request-URI is an absoluteURI, the host is part of the Request-URI. Any Host header field value in the request MUST be ignored.
  2. If the Request-URI is not an absoluteURI, and the request includes a Host header field, the host is determined by the Host header field value.
  3. If the host as determined by rule 1 or 2 is not a valid host on the server, the response MUST be a 400 (Bad Request) error message.

http_host

http_host 是请求头中的 Host 的值,注意它并不会从请求行中获取该值。
官方描述如下 (ref):

arbitrary request header field; the last part of a variable name is the field name converted to lower case with dashes replaced by underscores.

以下例子 http_host 就等于 www.w3.org 。

  1. GET /pub/WWW/TheProject.html HTTP/1.1
  2. Host: www.w3.org

proxy_host

proxy_host 是 proxy_pass 指令所设置的服务器名称(IP 或域名)和端口。
官方描述如下 (ref):

name and port of a proxied server as specified in the proxy_pass directive;

另外,在没有使用 proxy_set_header 指令重写请求头 Host 的情况下,Nginx 默认会将 Host 设置为 proxy_host ,如下所示是 Nginx 的默认行为:

  1. proxy_set_header Host $proxy_host;
  2. proxy_set_header Connection close;

以下配置代理的方式,因为 Host 会被设置成 192.168.0.2,所以后端就无法获取前端的真实的 Host 信息,这并不能很好满足后端可能存在多个站点的情况。

  1. proxy_set_header Host $proxy_host;
  2. proxy_pass http://192.168.0.2:80;

所以,一般我们会像下面这样配置,这种方式会将前端的 Host 作为请求后端服务器的 Host 请求头,将 Host 转发下去,因此后端服务器就能根据 Host 判断客户端是访问哪个站点。

  1. proxy_set_header Host $http_host;
  2. proxy_pass http://192.168.0.2:80;

总结

  • host 会优先从请求行的 Host、请求头中的 Host 字段中获取,如果都没有则等于跟此次请求所匹配的 server_name,如果三者都没有,则为空。
  • http_host 等于 HTTP 请求头中的 Host 字段,如果没有则为空。
  • proxy_host 等于 proxy_pass 指令所设置的服务器名称(IP 或域名)和 port。

在代理场景下,应该使用哪一个变量作为请求头的 Host,官方文档有比较清楚的说明,请参考

体会

熟悉 HTTP 协议,对于理解 Nginx 的工作过程实际上有很大帮助。

参考文献