97

What is the difference between the three Nginx variables $host, $http_host, and $server_name?

I have a rewrite rule where I'm not sure which one I should be using:

location = /vb/showthread.php {
    # /vb/showthread.php?50271-What-s-happening&p=846039
    if ($arg_p) {
        return 301 $scheme://$host/forum/index.php?posts/$arg_p/;
        }

I'm looking for an answer that doesn't just say 'use ___ variable in your rewrite rule' but also explains the theoretical differences between them.

Jeff Widman
  • 2,675

5 Answers5

107

You should almost always use $host, as it's the only one guaranteed to have something sensible regardless of how the user-agent behaves, unless you specifically need the semantics of one of the other variables.

The difference is explained in the nginx documentation:

  • $host contains "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"
  • $http_host contains the content of the HTTP "Host" header field, if it was present in the request
  • $server_name contains the server_name of the virtual host which processed the request, as it was defined in the nginx configuration. If a server contains multiple server_names, only the first one will be present in this variable.

Since it is legal for user-agents to send the hostname in the request line rather than in a Host: header, though it is rarely done except when connecting to proxies, you have to account for this.

You also have to account for the case where the user-agent doesn't send a hostname at all, e.g. ancient HTTP/1.0 requests and modern badly-written software. You might do so by diverting them to a catch-all virtual host which doesn't serve anything, if you are serving multiple web sites, or if you only have a single web site on your server you might process everything through a single virtual host. In the latter case you have to account for this as well.

Only the $host variable accounts for all the possible things that a user-agent may do when forming an HTTP request.

Michael Hampton
  • 252,907
7

I would like to add another important point not mentioned in the accepted answer.

$host do NOT have port number, while $http_host does include the port number.

edit: not always.

I set up a header "add_header Y-blog-http_host "$http_host" always;"

Then curl -I -L domain.com:80 (or 443) and the header doesn't show a port number at all. Verified with nginx-extra 1.10.3. Is is because it's common http(s) ports or nginx configuration? This comment just to say things do not always behave the way you think.

2

TL;DR: If you don't use regular expressions in server_name you can ignore this answer .

Since this aspect hasn't been mentioned at all in other answers, but it's something that I found somewhat annoying: I use $host in the creation of my log file names, like so:

access_log /var/log/nginx/access_$host.log

Alas, if you use a regular expression in your server_name stanza like:

server_name ~^(?<subdomain>foo|bar|baz)\.example.net;

Your $host will actually end up containing the regular expression like so (yep, including the backslash and everything):

access_~^(?<subdomain>foo|bar|baz)\.example.net.log

I for one found this very annoying and ended up changing my regular expression to:

server_name ~^(?<fulldomain>(?<subdomain>foo|bar|baz)\.example.net);

to capture the full name for reuse in the log file name. The respective stanza was then changed to:

access_log /var/log/nginx/access_$fulldomain.log
0xC0000022L
  • 1,576
  • 2
  • 24
  • 42
1

$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
    
  • Host request header field
  • The server_name (in Nginx config) matching a request, even if server_name is wildcard (Ex: server_name *.example.org;)

$server_name

$server_name is equals to server_name (in Nginx config), even if server_name is wildcard (Ex: server_name *.example.org;)

Host name from the request line

When open URL http://host.name/test/ ...

Most browser send the request like this

GET /test/ HTTP/1.1
Host: host.name

Most browser doesn't send the request like this

GET http://host.name/test/ HTTP/1.1

Ref: ngx_http_core_module, Nginx $host validation

1

I also struggled with this for a while. It became clear when I understood that $http_XXXXX refer to all declared header variables.

So $http_user_agent, $http_referer are "USER AGENT", "REFERER" referenced in lower case and underslash. This explained me where the hell $http_upgrade was coming from in many NGINX configuration samples.

Read it from https://stackoverflow.com/questions/15414810/whats-the-difference-of-host-and-http-host-in-nginx

luison
  • 271