As with one of those security headers, HSTS seemed as a no brainer at first. You send that special HTTP header and browsers remember that your website should be accessed via HTTPS only:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
This alone ensures that even if browser sees an http://
link, it will use https://
instead.
But is there more to it?
Few years later, I wanted to find out the rights and wrongs about my HSTS implementation, and how it really works. I think I’ve asked myself a couple of questions that many people ask themselves.
Q. 1. Technically how and what is the point of HSTS? My website has HTTPS redirect already!
Suppose that you are a random visitor of your own website. You type http://example.com and the server redirects you to https://example.com.
During that seemingly small amount of time when you access http://example.com (even for a redirect), a “man in the middle” attack is possible over unencrypted connection. That “man” (a hacker) can spoof server response and present something entirely different, thus eventually tricking you into sharing the access credentials from the site.
This will happen any time you click or otherwise go to http://example.com, despite existing HTTPS redirect.
Now, if you are to employ the HSTS header, the “main in the middle” attack is still possible. But only for the very first hit to http://.
There is always a probability that your very first hit to the http://
link of the website is being surveilled by that “main the middle”, with or without HSTS.
If you use the HSTS, you basically reduce the chance of “man in the middle” attack for subsequent visits to your website. By the time that you got man in the middle (since your first website visit) – your browser will likely already have “HTTPS” memory of your site and it will only talk to it via secure channel. Whether it hits an http://
or https://
link, it will always communicate via https://
.
So that’s what HSTS is really all about – reducing the chances of credentials leak.
Q. 2. What is HSTS preload and do I need it?
So if you have read my explanation of the HSTS, you see that it doesn’t eliminate insecure HTTP in entirety. The very first http://
request (resulting in https://
redirect, and further making browsers remember to use https://
only) is still subject to man in the middle attack.
To reduce the attack chances further, the HSTS preload list is used by majority of modern browsers. Basically it is a list of domain names that is hardcoded to browsers, telling them to use only https://
for those domains. The good thing is that it’s “telling them” to use only HTTPS with your website even before they ever visit and before even getting to see your HSTS header.
So this is another level of reducing the chances for hackers and you really want to use HSTS preload and make sure you’re listed.
The obvious reason aside for being listed (security), there is another one: if you’re HSTS preloaded, then there’s no actual http->https redirect, even for the first visit to your website.
Double redirect and speed
If you try to submit your domain to preload list and use the www
prefix for canonical domain for your website, you’d get a message like this:
You go and search for answers to make the right decision that is partially correct:
We should redirect HTTP requests to their HTTPS equivalent before any canonicalization steps (adding
or removing “www”), and send the HSTS header from any domain or subdomain that is meant to be only accessible over HTTPS.
To be correctly preloaded, the following are the proper patterns.
If our website canonical address includes www. prefix:
In this case, your canonical domain is www.example.com
. If a website visitor goes to http://example.com and you redirect directly to secure canonical which is https://www.example.com, then any next time they try to visit http://example.com again, this won’t be secure. Why is because your browser have not seen HSTS header for example.com
, it only have seen it for www.example.com
.
Thus the proper redirects flow is: http://example.com -> https://example.com (with HSTS header) -> https://www.example.com/ (with HSTS header).
Only in this way, all of your addresses will be secured (both www
and non-www
).
Shortly, you have to:
- send HSTS header with
preload
directive (and ideally withincludeSubDomains
) - redirect non-www to
https://
then to https://www (2 redirects)
Nginx:
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
more_set_headers "Strict-Transport-Security: max-age=31536000; includeSubDomains; preload";
ssl_certificate ...;
ssl_certificate_key ...;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 80;
server_name www.example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 443 ssl http2;
more_set_headers "Strict-Transport-Security: max-age=31536000; includeSubDomains; preload";
ssl_certificate ...;
ssl_certificate_key ...;
server_name www.example.com;
... main website directives go here
}
If your website canonical address does not include www. prefix:
In this case, your canonical domain is www.example.com
. If a website visitor goes to http://www.example.com and you redirect directly to secure canonical which is https://example.com, then any next time they try to visit http://www.example.com again, this won’t be secure. Why is because your browser have not seen HSTS header for www.example.com
, it only have seen it for example.com
.
This time, to address this, we simply ensure includeSubDomains
directive in HSTS header.
Shortly, you have to:
- send HSTS header with
preload
andincludeSubDomains
directives - redirect
www.
directly to https://example.com is fine
Nginx:
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
more_set_headers "Strict-Transport-Security: max-age=31536000; includeSubDomains; preload";
ssl_certificate ...;
ssl_certificate_key ...;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 80;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 443 ssl http2;
more_set_headers "Strict-Transport-Security: max-age=31536000; includeSubDomains; preload";
ssl_certificate ...;
ssl_certificate_key ...;
server_name example.com;
... main website directives go here
}
Alternatively and very rarely you don’t want to preload all subdomains. Then you basically have to follow the www.
earlier pattern of double redirects.
That said, the HSTS preload list basically requires includeSubDomains
, so I would not recommend going this route. It is always good to secure all the subdomains and there are no reasons not to.
Implications of double redirect (www.)
As detailed above, you have to configure two redirects if your primary website address has the form of www.example.com
. If a user types example.com
in their browser, you have to redirect them to https://example.com and “show” them HSTS header which will apply to example.com
, then redirect them to https://www.example.com, this time to show another HSTS header that will apply to www
subdomain. But nobody likes multiple redirects. I don’t like too!
I’ve been giving a thorough thought on this. Do I want SEO or security, is there a compromise? But thinking about it leads me to conclusions that in this case the single extra redirect is definitely worth the security, and its performance hit is negligible for these reasons:
- Users who type
example.com
directly in their browsers are typically the ones who already visited your website. Which means, their browser have already seen the HSTS header and this on its own eliminates one of the redirects. Not to say that browser should have cached the non-www to www redirect. So things are going to be as quick as no redirects at all for them. - The redirects on their own are fast.
- All search engines are smart enough about permanent redirects. Plus you must have linked using proper canonical URL in your ads or otherwise external links, haven’t you? There is little or no issue with landing pages SEO.
- Finally, the fact that your domain is preloaded means that there will be no http->https redirect at all. If a domain is preloaded and users type
example.com
, then the browser will only incur single redirect https://example.com -> https://www.example.com (canonical redirect). Of course it’s essential to submit the domain to preload list to make sure that this is how things will work.