nginx: the difference between $host, $http_host & $hostname and priorities of $host

Nginx has a lot of system variables, this makes it really flexible which is great news. It also inevitably introduces some complexity, this is generally not a huge deal but in the odd place, the nginx docs are somewhat lacking which means you have to either google the issue you’re facing or investigate.

I hit this situation in my current project for something simple yet fundamental. What I needed to have some clarity on is the $http system variable. Let’s get one issue sorted straight off, the docs state that two similar nginx system variables $host and $hostname are:

$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

$hostname
host name

So $hostname is a little scant on detail. It’s (at least on *nix) the FQDN machine hostname that you’d get by running (on the shell):

#hostname -f

Good. Let’s move on.

So, the more useful $host variable highest priority match looks to be 100% synonymous with the requested host in the HTTP request but actually, it’s a normalised version thereof. The normalisation of $host is to make it lowercase and to remove the port designation (e.g. :80).

It seems that there’s also another, undocumented and in fact higher priority for $host (I tested nginx 1.9.2, the current latest) which I found experimentally: if you’re using nginx with a proxy_pass directive to an nginx upstream, $host appears to take the name of the upstream as the highest priority.

For example, I have a VM running on my laptop which is accessed from my host OS via port forwarding (:9080 on host to :80 on guest), the :80 listener on the guest is nginx which has a server block for test.example.com which does an HTTP proxy_pass to an upstream named “prx” which in this case simply listens on :9999 and serves local, static files. When I access the guest from my host OS (via a record in /etc/hosts: 127.0.0.1 test.example.com) I see that $host contains “prx”.

add_header $host always;

# OUTPUT: prx

It’s unclear whether this is a result of my host to guest port forwarding or whether it’s a new priority which hash’t yet been documented or perhaps unintended functionality. Maybe in fact, I did something wrong. Comments are very welcome!