Nginx – Why does Nginx ignore trailing dots in the “Host” header

domain-name-systemhttp-headersnginx

I've been running across an odd behavior in Nginx with requests that have a trailing dot in the hostname, i.e. domain.com. rather than domain.com alone. I set up a simple server config to test, like so:

server {
    listen 80;
    server_name example.com.;
    root /var/www/example;
    index server1.txt;
}

server {
    listen 80;
    server_name example.com;
    root /var/www/example;
    index server2.txt;
}

Initially, I expected that requests made to example.com. would be sent to the first block and requests made to example.com would be sent to the second. Requests whose Host header didn't match any block, e.g. www.example.com, I expected to be sent to the first block again as it was the implicit default.

However, upon testing I discovered that requests made to example.com. were sent instead to the second block. After messing around for a good while with various alternate names, regular expressions etc. I decided to write the $host variable to a custom header so I could look at it. Turns out that Nginx is actually dropping the trailing dot from the $host value. As far as I can tell a request received at example.com. is considered identical to one received at example.com, at least as far as server-selection goes. This seems undesirable, as trailing dots can result in various errors..

To make matters even more confusing, after some googling I found the Nginx changelog page, which has in the 0.1.29 changelog:

Bugfix: nginx did not take into account trailing dot in "Host" header line.

Then, years later, the 1.5.9 changelog says:

Bugfix: resolver did not understand domain names with a trailing dot. Thanks to Yichun Zhang.

Although I'm not sure whether the "resolver" refers to a component of Nginx that is in play when it receives a request. (From reading the docs, it sounds like maybe the resolver doesn't do anything unless a request is being forwaded to some other host, whose name must then be resolved.)

What's going on here? Should Nginx be dropping the trailing dot when it evaluates server names? If this is intended behavior, shouldn't the trailing dot also be dropped from the server_name parameter to yield a "conflicting server name" error?

I know that a domain name without a trailing dot is technically a "relative" domain name rather than "absolute," although most people these days seem to treat them as all relative to the . zone so it doesn't make a practical difference. But shouldn't Nginx at least be able to make this distinction if desired?

Finally, is there any better way to catch and redirect requests made to example.com. than adding if ($http_host = 'domain.com.')? I've been told that this is inefficient, as it requires the Host header to be evaluated twice.

Best Answer

At the DNS level, example.com and example.com. are the same name. That does not just mean that they are supposed to be treated the same, it means that both of them will encode to the exact same sequence of octets in a DNS packet. Trying to treat them as different in protocols that use DNS (like, for example, HTTP) is guaranteed to cause confusion and problems.

If a piece of software uses names from DNS and treats the same name with and without a trailing dot as different, that piece of software has a bug.