93

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.

2
  • I realized later I didn't even need to specify $scheme and $host... return 301 /forum/index.php?posts/$arg_p/; works fine. Commented Jul 17, 2015 at 8:06
  • Most browser would work with relative URL in redirect, but the standard (w3.org/Protocols/rfc2616/rfc2616-sec14.html) requires absolute url in the Location header.
    – Cthulhu
    Commented Jul 17, 2015 at 9:16

5 Answers 5

99

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.

5
  • 5
    On the other hand, the $server_name is safe when Host: field from UA may contain arbitrary content.
    – Cthulhu
    Commented Jul 17, 2015 at 9:13
  • 1
    Is $http_host renamed to $hostname ? I can not find such variable in Nginx doc. $hostname is the most similar one I guess. Commented Feb 23, 2016 at 3:00
  • 4
    @darkbaby123 No, it was not renamed to anything. See the documentation. Commented Feb 23, 2016 at 3:15
  • 2
    Ah now I understand what http_<name> variable means. Thank you! Commented Feb 23, 2016 at 3:38
  • 1
    Could be $ssl_server_name should be mentioned also. It "returns the server name requested through SNI (1.7.0)". Commented Oct 28, 2020 at 10:56
6

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.

1
  • Is is because it's common http(s) ports or nginx configuration? Yes, curl won't add the common port number to the Host header even if you invoke it that way. However there is no warranty that some other program will not add that number if being invoked similar way. And of course you can manually overwrite the default Host header using curl -I -H 'Host: domain.com:80' ... Commented Jun 10, 2022 at 10:01
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
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

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .