Forced HTTPS and HSTS with www removal issue


#1

My current .htaccess file is this:

[code]Header set Strict-Transport-Security “max-age=31415926; includeSubDomains; preload” env=HTTPS

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301][/code]

HSTS is being sent in the header for both the naked domain and the www subdomain. But when I add lines to .htaccess to remove the www…

[code]Header set Strict-Transport-Security “max-age=31415926; includeSubDomains; preload” env=HTTPS

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

RewriteEngine on
RewriteCond %{HTTP_HOST} ^www.(.+)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L][/code]

…everything works except HSTS is not being sent in the header for the www subdomain. The naked domain sends HSTS in the header just fine.

Any ideas on how to get the www subdomain to send HSTS? I think my .htaccess needs something fixed to get this to work.

By the way, I’ve set the hosting panel to allow both naked and www.


#2

I managed to fix this. Here’s how I did it.

First, I need to explain the env=“HTTPS” at the end of the Header directive. The very last sentence of section 7.2 in RFC 6797 (HTTP Strict Transport Security [HSTS]) states: “An HSTS Host MUST NOT include the STS header field in HTTP responses conveyed over non-secure transport.” Therefore, in order to be completely RFC 6797 compliant, you must include this argument in the Header directive for Apache HTTP Server (Dreamhost uses Apache HTTP Server for its shared hosting).

Second, I decided to stop looking at the documentation for the Apache HTTP Server’s mod_rewrite module (here), which implements the RewriteRule directive, and look at the documentation for the mod_headers module (here), which implements the Header directive. That’s where I stumbled upon the condition argument for the Header directive. The default for the condition argument is onsuccess, but that doesn’t work for a 301 redirect which by its very being is not a successful (2xx) response. So the optional always condition needed to be set in Header set Strict-Transport-Security “max-age=31415926; includeSubDomains; preload” env=HTTPS for the HSTS Header directive to operate on the www-remove RewriteRule.

Third, although the www-remove RewriteRule will only ever get to respond to HTTPS requests (by virtue of the preceeding forced-HTTPS RewriteRule), RewriteRules always reset the environment of the response header to HTTP (not HTTPS). Because of this and the fact that the HSTS Header directive is set to require an HTTPS environment, the HSTS Header directive fails to operate on the www-removal RewriteRule. So, the www-remove RewriteRule needs to be modified to set the post RewriteRule environment to HTTPS. I took a look at the documentation for the mod_rewrite module (here) and saw that RewriteRule allows for flags to be set, one of which (E) allows for environment variables such as HTTPS to be set. Once I added E=HTTPS to my flags for the www-remove RewriteRule, the HSTS Header directive could now operate on the www-remove RewriteRule directive. With that set, the secure www subdomain was now responding with a 301 redirect and HSTS in its header.

RewriteEngine on RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] RewriteRule ^ https://%1%{REQUEST_URI} [E=HTTPS,L,R=301]

PS: I’d like to thank TruboFlash, whose personal blog entry on the visibility of environment variables between various Apache HTTP Server’s modules’ directives gave me the surprisingly never before documented insight needed to connect the dots for this problem.