Skip navigation

HSTS + Apache default sites: a cautionary tale

Posted Dec 26, 2016

OK, so the title of this one might be a bit overblown given that this is actually a pretty marginal case, but hey, it's a click-bait world. :)

HTTP Strict Transport Security

So… what the heck is HSTS anyway? Well, let's take a look:

HTTP Strict Transport Security (HSTS) is an opt-in security enhancement that… will prevent any communications from being sent over HTTP to the specified domain and will instead send all communications over HTTPS.

Translation: HSTS is an option on your web server that tells the browser to only ever use HTTPS on that site. Any time an HSTS-compliant browser sees a link like this:

<a href="">Link</a>

…it will ignore the http:// and send the request to, along with any other requests to that domain.

This is great for security, since it eliminates the potential for certain man-in-the-middle attacks where a user clicks an HTTP link (either an old one, or one maliciously-inserted somewhere) on a site that should only be using HTTPS.

MDN has a pretty good write-up if you'd like to learn more about HSTS. For now, the important thing to note is the way that HSTS is specified—through an HTTP header, like so:

Strict-Transport-Security: max-age=31536000

This example will tell the browser to only use HTTPS on this domain for the next 31,536,000 seconds (1 year).

The one real drawback to HSTS, of course, is if you ever decide to stop using HTTPS. I won't get into the reasons why you might want to do this, but in this case we're going to look at a scenario where it's possible to accidentally start using it in the first place.

Apache Default Site

If you're like me, you often use Apache to serve multiple websites. What you may not be aware of though, is that one of these Virtual Hosts will be set as your default site—the one that will be served whenever Apache receives a request that it doesn't have a matching host for.

So when the heck would it receive a request like that? Well, like I said at the beginning, this is a bit of an edge case, but here goes: when you're setting up a site for the first time.

Let's say you're putting up a new site. Maybe the first thing you do is buy the domain name and point it at your server's IP. Next you wait a few minutes for the DNS to propagate and then test to see if it works. Alternatively, you might just add the domain name to your local hosts file while you're in development. Either way, you could end up with your browser making a request for to your web server which doesn't have a corresponding Virtual Host set up. This results in your default Virtual Host being served.

So here enters the problem: what if that default Virtual Host has HSTS enabled? Well, Apache will go ahead and attach the HSTS header to the response—even though it's serving the wrong domain.

The result? The first time you attempt to go to, you get one of these:

A browser error message with a red lock icon stating: "Your connection is not private. Attackers might be trying to steal your information from (website name obscured), for example, passwords, messages, or credit cards."
Pro tip: don't let this happen to your users.

Oh, did I say the first time you go to the site? Oh yeah—I also meant every time after that because you've now set HSTS on a domain that doesn't actually use HTTPS.

That's right. No matter how many times you specifically type into your address bar (or how hard you rage-pound your keyboard while doing it), your browser will petulantly insist on going to Every. Single. Time.


Great. What do I do now?

Of course, if you're planning on deploying HTTPS on the new site anyway, doing that will take care of the problem for you. If not, here are instructions for resetting HSTS in most of the major browsers.

Unfortunately, Edge/IE aren't covered as it appears they don't offer a means of doing this. If you're using one of them, your only option is to temporarily change Apache's HSTS setting to max-age=0 and visit the page again.

That will at least take care of your immediate problem. Next, let's look at how to prevent this from occurring in the first place.

The solution

Most guides to implementing HSTS on Apache will tell you to simply add the following to your Virtual Host declaration (possibly with some other options like includeSubdomains—refer to the HSTS page on MDN I mentioned for more on that stuff):

Header always set Strict-Transport-Security "max-age=31536000"

This will set HSTS on this Virtual Host (and any other request that get served by it if it's the default) under any and all conditions. We're going to just modify this a bit to make sure that we're only setting HSTS on the domain that we actually want it on:

SetEnvIf Host ^$ mydefaultsite
Header always set Strict-Transport-Security "max-age=31536000" mydefaultsite

So what's going on here? In the first line, we're conditionally setting an Apache Environment Variable. Basically we're telling Apache that if the domain name is, then it should consider the "mysite" environment variable to be true.

In the second line, we're just slightly modifying our HSTS header so that it will only get sent if "mysite" is true (i.e., if we're serving a response on This way, if/when a request comes in for (which doesn't have a Virtual Host declaration yet), Apache won't set HSTS on the response. Hooray!

Did this post help you? Have you found any other issues when using HSTS? Let me know!