While inspecting traffic logs for my website in redirection.io recently, I noticed several requests logged on the lacot.org.
domain name. Did you see the final trailing dot in the hostname? I first thought it was a bug in the logging platform, but I was surprised to learn that it is perfectly possible to add a dot at the end of a domain name. To be more precise, I already knew about trailing dots in domain names, but never thought about checking how it was handled in web browers and in HTTP traffic.
Short anwser: Yes, you can. The domain name system (DNS) allows you to put a dot at the end of a domain name. This is not a common practice (at least in the "Web browsing" world), but it is possible. Adding a dot at the end of a domain name turns it into a specific notation that is called the "Fully Qualified Domain Name" (FQDN). A FQDN is a domain name that specifies its exact location in the tree hierarchy of the Domain Name System (DNS). It specifies all domain levels, including the top-level domain and the root zone.
A website domain, eg. www.example.com
, is made of several parts:
www
in this case;example
;com
.The syntax www.example.com
is the most commonly used on the Web, and it relies on the fact that your local DNS resolver will resolve com
as the top level domain. If you need or want to be more explicit, you can add a trailing dot, eg. www.example.com.
. In this case, the dot at the end of the domain name represents the root of the DNS hierarchy, ie. the top-level DNS zone in the hierarchical tree that is served by the root name servers. Therefore, using the FQDN syntax disambiguishes the fact that the request is for www.example.com.
, not for an other address. For this request, the IP address will be resolved as the one configured in the A
record by the owner of the example.com.
domain name, and it will not use any other configuration that could have been setup in any other local network DNS server.
To summarize:
example.com.
will always resolve example.com
as a fully qualified domain name. In a web browser, sending a request to http://example.com.
will get you on a web page sevred by the IP address defined in the A
record of the domain name "example.com".example.com
is a bit less precise - it will resolve the name "example.com" relatively to the DNS servers chain. In other words, a DNS server queried for "example.com" can search for a local definition of example.com and may decide to forward this request to a more authoritative DNS node.With the syntax example.com
, the domain name is not in its Fully Qualified form. Imagine a company local network area - say, acme.com
- in which several web services are hosted, with different hostnames. It is perfectly possible to omit the LAN domain when adressing such services: ssh audio
will work the same way like ssh audio.acme.com
.
And you get it: setting up a machine with the hostname admin.finance.example.com
will make its service locally reachable at both the adresses http://admin.finance.example.com
or http://admin.finance
. In the meantime, ædmin.finance
also exists as a fully qualified domain name (ie. ædmin.finance.
, not an alias for admin.finance.example.com.
).
In other words, a customer on this local network who would type "http://admin.finance" in their web browser would be redirected to the service hosted on the machine with the hostname admin.finance.example.com
. This is because the local DNS resolver will append the local domain name to the hostname, and will resolve the IP address of the machine. This is a common practice in local networks, and it is called "search domain".
So, there's a possible confusion here: example.com
and example.com.
are not the same thing. The first one is a domain name, the second one is a fully qualified domain name.
Most of the time however, a domain name holder will not make a difference between the two. Also network administrators usually take care not to setup machines with names that could be confused with a domain name. But it is possible to have a machine with a name that is the same as a domain name, and in this case, the trailing dot will make the difference.
We are left in a sort of dilema here:
example.com
and example.com.
be indexed by search engines. This would be considered as duplicate content, and it could be bad for the website's ranking.example.com
and example.com.
.The usual way to handle this would be to setup a redirect from one to the other. But which way?
example.com
to example.com.
: this would be technically the best way to handle this, as it would make sure that the website is always reached by its fully qualified domain name. But this would be confusing for users, as they are not used to type a dot at the end of a domain name.example.com.
to example.com
: this would be the best way to handle this for users, as they are not used to type a dot at the end of a domain name. The rare requests which would arrive on the FQDN would be redirected to the usual domain name, without the trailing dot. But this might be technically incorrect, and would left users that are on a local network with a search domain setup unable to reach the real website (with the example above, a request to http://admin.finance.
would be redirected to http://admin.finance
, that the local network DNS resolver would resolve to the local-network admin.finance
machine, and not the real public website).Actually, a good solution might be to serve a static page on the FQDN, that would perform a client-side fetch request to the non-FQDN domain name, check if the response originates from the real website, and then redirect the user to the non-FQDN domain name if it is the case. If it is not the case, it could load the content to display from the FQDN domain name (with a cookie or some other way to avoid duplicate content issues). This would however be a bit tricky to setup, and would require some JavaScript code to be executed on the client side.
Architectural choices are always a tradoff, and sometimes you are left to choose the least bad solution. For this reason and for simplicity sake, I chose to setup a redirect from the FQDN to the non-FQDN domain name. In my opinion, it is the most common and user-friendly way to handle this case. Yet it will let some users unable to reach my real website, if:
lacot.org
)This is a rare case, and I think it is acceptable in this context, though it could lead to some confusion for users (or even make the way easier for phishing attacks!). I may not go with the same advice for a banking website, for example :-)
However, the "redirect everything" solution has also major advantages (see the "Browsers behavior" chapter below).
There's no easy answer to this question, as it depends on the web server or reverse-proxy you are using.
example.com
and example.com.
. In both cases, the server will serve the same content. Provided you have configured a VirtualHost for the domain name example.com
, the server will serve the content of this VirtualHost for both syntaxes. Without any specific configuration, you are at risk of having duplicate content issues.example.com
and example.com.
. If you have a VirtualHost for example.com
, the server will serve the content of this VirtualHost for both syntaxes. Without any specific configuration, you are at risk of having duplicate content issues.example.com
domain name, the server will not serve the content for the example.com.
FQDN. You will have to add a specific configuration for the FQDN domain name (for example, to redirect requests to the non-FQDN domain name). This is a good practice, as it will avoid duplicate content issues.Browser do consider that the domain name example.com
and example.com.
are different. They will not share the same cache, and they will not share the same memory spaces within the browser. This is a normal behavior, as they're different hostnames, but it can also lead to some unexpected behaviors. For example, the user may not expect to be logged out when switching from one domain name to the other. Here are the impacts of serving a content on both a domain name and its FQDN version:
So, to summarize the risks associated with an improper handling of trailing dots in domain names, you could face potential costs, security, SEO, and user experience issues.
Here is a summary of how several large websites handle trailing dots in domain names. It is based on a quick test I made with Symfony's HttpCLient, and it may not be perfectly exhaustive.
Domain | http |
https |
http + www |
https + www |
Remarks | |
---|---|---|---|---|---|---|
amazon.com. |
301 |
400 |
301 |
200 |
😡 | The root domain is not reachable in https . The www version in https serves content, but with a separate session from https://www.amazon.com , which means that you can be connected on both with separate accounts at the same time. |
apple.com. |
404 |
404 |
301 |
200 |
😡 | The root domain is not reachable in both http and https . The www version in https serves content, but no asset is loaded as their URL violates the Content-Security-Policy of the website. |
bbc.com. |
301 |
301 |
302 |
200 |
⚠ | The www version in https serves content instead of redirecting to https://www.bbc.com . |
bing.com. |
400 |
400 |
200 or 307 |
SSL error and 200 |
😡 | The root domain returns an HTTP error. The www version in https does not have a valid certificate. For clients that do not check the certificate, a response is served with a 200 status code instead of redirecting to https://www.bing.com . |
cloudflare.com. |
301 |
301 |
301 |
200 |
⚠ | The www version in https serves content instead of redirecting to https://www.cloudflare.com . |
developer.mozilla.org. |
301 or 307 |
SSL error |
N/A |
N/A |
😡 | The www. version does not exist. The root domain in http redirects to the non-FQDN domain name or to the https version of the FQDN, depending on whether the client supports HSTS or not. The root domain in https does not have a valid certificate. |
duckduckgo.com. |
301 |
200 |
301 |
301 |
⚠ | The root domain in https serves content, but no asset is loaded as their URL violates the Content-Security-Policy of the website. |
ebay.com. |
301 |
301 |
301 |
200 |
⚠ | The www version in https serves content instead of redirecting to https://www.ebay.com . |
free.fr. |
301 |
301 |
301 |
302 |
✅ | |
github.com. |
301 |
301 |
301 |
301 |
✅ | |
gitlab.com. |
301 |
302 |
301 |
302 |
✅ | |
gnu.org. |
301 |
301 |
200 or 307 |
200 |
⚠ | The www version in https serves content instead of redirecting to https://www.gnu.org , or it redirects to the https version of the FQDN if the client supports HSTS. The www version in https serves content instead of redirecting to https://www.gnu.org . |
google.com. |
301 |
301 |
301 |
301 |
✅ | |
instagram.com. |
301 |
400 |
301 |
400 |
😡 | The http versions are redirected to the https versions, but these return a 400 error (with, strangely, an HTML content that states "5xx Server Error" 🙃). |
jolicode.com. |
301 |
301 |
301 |
301 |
✅ | |
lacot.org. |
301 |
301 |
301 |
301 |
✅ | |
lemonde.fr. |
301 |
301 |
301 |
200 |
⚠ | https://www.lemonde.fr./ servers content instead of redirecting to https://www.lemonde.fr/ . |
linkedin.com. |
400 or 307 |
400 |
404 or 307 |
404 |
😡 | The root domain returns a 400 error. The www version in https returns a 404 error. |
medium.com. |
301 |
404 |
301 |
301 |
😡 | The root domain in https returns a 404 error, while all other configurations redirect to https://medium.com . |
microsoft.com. |
404 or 307 |
SSL error |
302 |
302 |
😡 | The root domain returns a 404 error. The SSL certificate is invalid for the https version of the root domain. |
mozilla.org. |
302 |
SSL error |
301 |
200 |
😡 | The root domain in https does not have a valid certificate. The www version in https serves content instead of redirecting to https://www.mozilla.org . |
netflix.com. |
301 |
301 |
301 |
301 |
✅ | |
nytimes.com. |
301 |
301 |
301 |
200 |
⚠ | The www version in https serves content instead of redirecting to https://www.nytimes.com . |
ovhcloud.com. |
301 |
SSL error and 503 |
400 |
400 |
😡 | The SSL certificate is invalid for the https version of the root and the www domains. Additionally, the http version of the www domain returns a 400 error. |
php.net. |
301 |
301 |
301 |
200 |
⚠ | The www version in https serves content instead of redirecting to https://www.php.net . |
reddit.com. |
301 |
301 |
301 |
200 |
⚠ | The www version in https serves content instead of redirecting to https://www.reddit.com . |
stackoverflow.com. |
301 |
400 |
301 |
400 |
😡 | The https versions return a 400 error. |
symfony.com. |
301 or 307 |
502 |
301 or 307 |
502 |
😡 | The https versions return a 502 error. |
twitter.com. |
404 |
404 |
404 |
404 |
😡 | Twitter (and X) does not work with a Fully Qualified Domain Name. |
ubuntu.com. |
302 |
SSL error and 200 |
302 |
SSL error and 200 |
😡 | The https versions do not have a valid SSL certificate. For clients that do not check the certificate, a response is served with a 200 status code instead of redirecting to https://ubuntu.com |
vercel.com. |
308 |
SSL error and 200 |
308 |
SSL error and 308 |
😡 | The https versions do not have a valid SSL certificate. For clients that do not check the certificate, a response is served with a 200 status code instead of redirecting to https://vercel.com . |
w3.org. |
301 |
301 |
301 |
200 |
⚠ | The www version in https serves content instead of redirecting to https://www.w3.org . |
wikipedia.org. |
301 |
301 |
301 |
200 |
⚠ | The www version in https serves content instead of redirecting to https://www.wikipedia.org . |
wordpress.com. |
301 |
301 |
301 |
301 |
✅ | |
xkcd.com. |
301 |
200 |
301 |
200 |
⚠ | The https versions serve content instead of redirecting to https://xkcd.com . |
youtube.com. |
301 |
301 |
301 |
301 |
✅ | |
zend.com. |
301 |
301 |
301 |
200 |
⚠ | The www version in https serves content instead of redirecting to https://www.zend.com . |
This table gives some insights on how trailing dots in domain names are supported. Basically, there are often mistakes:
As a conclusion, even if it is not a silver-bullet solution for all cases, here are some recommendations on how to handle trailing dots in domain names:
https
version of your non-FQDN domain name;www
version): this is important, as it will avoid SSL errors for clients that check the certificate;2xx
responses on the FQDN domain name: it could generate cache issues (and even security issues), increase costs, etc.I implemented the trailing-dot domain name redirection using the "Redirect a Fully Qualified Domain" redirection.io recipe, but you're free to go your way :-)