Site icon GetPageSpeed

Dissecting HTTPS redirect requirements of HSTS

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:

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:

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:

Exit mobile version