In Nginx, what's the difference between variables $host and $http_host.
4 Answers
$host is a variable of the Core module.
$host
This variable is equal to line Host in the header of request or name of the server processing the request if the Host header is not available.
This variable may have a different value from $http_host in such cases: 1) when the Host input header is absent or has an empty value, $host equals to the value of server_name directive; 2)when the value of Host contains port number, $host doesn't include that port number. $host's value is always lowercase since 0.8.17.
$http_host is also a variable of the same module but you won't find it with that name because it is defined generically as $http_HEADER (ref).
$http_HEADER
The value of the HTTP request header HEADER when converted to lowercase and with 'dashes' converted to 'underscores', e.g. $http_user_agent, $http_referer...;
Summarizing:
- $http_hostequals always the- HTTP_HOSTrequest header.
- $hostequals- $http_host, lowercase and without the port number (if present), except when- HTTP_HOSTis absent or is an empty value. In that case,- $hostequals the value of the- server_namedirective of the server which processed the request.
- 
                    77$host is specifically **the first `server_name`** that is defined in the current server block. if you have multiple `server_name`s, only the first one will appear. – Jonathan Vanasco Mar 14 '13 at 16:36
- 
                    5True. In fact, it is quite typical to define: server_name example.com www.example.com; – glarrain Mar 14 '13 at 16:41
- 
                    1you can have multiple `server_name` directives too. if you happen to have a regex in the first one, that becomes the `$host` , and all sorts of ugly stuff can happen during rewrite rules. – Jonathan Vanasco Mar 14 '13 at 17:09
- 
                    7Does the `$server_name` variable equal the `server_name` directive's value or the actual server name that was selected if there were multiple `server_name` directives? – CMCDragonkai Mar 13 '14 at 20:03
- 
                    2@CMCDragonkai `$server_name` is always equal to the first value specified with the server_name directive. For example, with `server_name example.com one.example.com two.example.com;`, `$server_name` will always be "example.com", **regardless of which host the user has specified**. In fact, if you don't have a `default_server`, the host might be something completely different (like example.org). – ATLief Apr 02 '19 at 14:23
- 
                    2Links are kinda 404 now. – luckydonald May 20 '20 at 15:35
The accepted answer and its comments don't seem to be correct (anymore). The docs (http://nginx.org/en/docs/http/ngx_http_core_module.html#var_host) say that $host is
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
So $http_host is always the value of the Host header field. They might differ if the host in the request line (if specified) differs from the Host header field. Or if the Host header is not set.
server_name matches only the Host header field (http://nginx.org/en/docs/http/request_processing.html), so that $host may differ from the matched server_name.
 
    
    - 4,042
- 4
- 26
- 28
- 
                    yes, though it is important to note that `$http_host` has still the port included, while `$host` doesn't seem so. Some peculiar settings may need the port to behave well, otherwise they fall on standard settings (port 80/443). – Pier A Nov 28 '22 at 14:03
$http_host
$http_host always equals Host request header field
Host: example.org
$host
$host is in this order of precedence (from high to low):
- Host name from the request line
GET http://example.org/test/ HTTP/1.1
- Hostrequest header field
- The server_name(in Nginx config) matching a request, even ifserver_nameis wildcard (Ex:server_name *.example.org;)
Host name from the request line
When open URL http://example.org/test/ ...
Most browser send the request like this
GET /test/ HTTP/1.1
Host: example.org
Most browser doesn't send the request like this (but this is valid request)
GET http://example.org/test/ HTTP/1.1
Validation
Nginx testing config
server {
    listen       80;
    server_name  *.example.org;
    location / {
        default_type "text/plain";
        return 200 "[host] = $host";
    }
}
When all exist ...
$host = host name from the request line
curl http://127.0.0.1 -v \
  --request-target http://request.example.org/test/ \
  --path-as-is \
  -H "Host: host.example.org"
This command will
- Connect to 127.0.0.1
- Send request path as GET http://request.example.org/test/ HTTP/1.1
- Set Hostheader toHost: host.example.org
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET http://request.example.org/test/ HTTP/1.1
> Host: host.example.org
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.23.1
< Date: Fri, 21 Oct 2022 02:00:56 GMT
< Content-Type: text/plain
< Content-Length: 28
< Connection: keep-alive
<
* Connection #0 to host 127.0.0.1 left intact
[host] = request.example.org
When only Host header exist ...
$host = Host header
curl http://127.0.0.1/test/ -v \
  -H "Host: host.example.org"
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET /test/ HTTP/1.1
> Host: host.example.org
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.23.1
< Date: Fri, 21 Oct 2022 02:01:37 GMT
< Content-Type: text/plain
< Content-Length: 25
< Connection: keep-alive
<
* Connection #0 to host 127.0.0.1 left intact
[host] = host.example.org
When none exist ...
$host = server_name (in Nginx config)
# HTTP 1.1 must have Host header, so use HTTP 1.0
curl http://127.0.0.1/test/ -v -H "Host:" -0
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET /test/ HTTP/1.0
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.23.1
< Date: Fri, 21 Oct 2022 02:02:20 GMT
< Content-Type: text/plain
< Content-Length: 22
< Connection: close
<
* Closing connection 0
[host] = *.example.org
 
    
    - 16,239
- 8
- 58
- 54
- 
                    2Excellent answer. The problem is, that "Host name from the request line" has the highest priority and will route the request to a `server` (vhost) even if the request has a bogus `Host` header and does not match `server_name`. This is an issue because applications behind nginx tend to use the passed on `Host` header. It's a pity that `server_name` does not check the `Host` header (if exists in the request) if there was a hostname in "the request line" -- a rare and mostly malicious case. – minusf Oct 18 '22 at 08:59
Both $http_host and $host are variables used in Nginx configuration files, but they have slight differences in their usage and behavior.
- $http_host: This variable represents the value of the "Host" header in the HTTP request. It includes the port if specified by the client. For example, if the client sends a request to "example.com:8080", then- $http_hostwill be set to "example.com:8080". This variable is useful when you need to capture the exact host value from the client request.
- $host: This variable represents the name of the server requested by the client, without the port number. It only contains the hostname or IP address. For example, if the client sends a request to "example.com:8080", then- $hostwill be set to "example.com". This variable is commonly used for server configuration, especially when defining server names or redirects.
To summarize, the main difference between $http_host and $host is that $http_host includes the port number specified by the client, while $host does not. In most cases, you would use $host when you need to capture the hostname for server-related configuration, and $http_host when you need to capture the complete "Host" header value with the port included.
 
    
    - 81
- 5
 
    