92

My web application runs on a different number of hosts that I control. To prevent the need to change the Apache config of each vhost, I add most of the config using .htaccess files in my repo so the basic setup of each host is just a couple of lines. This also makes it possible to change the config upon deploying a new version. Currently the .htaccess (un)sets headers, does some rewrite magic and controls the caching of the UA.

I want to enable HSTS in the application using .htaccess. Just setting the header is easy:

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

But the spec clearly states: "An HSTS Host MUST NOT include the STS header field in HTTP responses conveyed over non-secure transport.". So I don't want to send the header when sending it over HTTP connections. See https://datatracker.ietf.org/doc/html/draft-ietf-websec-strict-transport-sec-14 .

I tried to set the header using environment vars, but I got stuck there. Anyone that knows how to do that?

Community
  • 1
  • 1
nielsr
  • 2,417
  • 2
  • 15
  • 16
  • Stack Overflow is a site for programming and development questions. This question appears to be off-topic because it is not about programming or development. See [What topics can I ask about here](http://stackoverflow.com/help/on-topic) in the Help Center. Perhaps [Super User](http://superuser.com/) or [Unix & Linux Stack Exchange](http://unix.stackexchange.com/) would be a better place to ask. Also see [Where do I post questions about Dev Ops?](http://meta.stackexchange.com/q/134306) – jww Nov 05 '16 at 18:13
  • 9
    @jww The .htaccess files are part of my web app repo and maintained by developers to get the desired behaviour of the application (e.g. caching, rewriting url's, and setting the right headers). The question is viewed nearly 20K times here on SO (and [apache], [.htaccess] and [mod-headers] tags are available). So I don't think it is off topic here. – nielsr Mar 17 '17 at 13:10
  • 1
    *"The .htaccess files are part of my web app repo and maintained by developers..."* - Neither are criteria for inclusion on Stack Overflow. A good sniff test is, can you show your code? In this case, the answer is NO. After examination, its just an Apache config question. *"The question is viewed nearly 20K times..."* - Stack Overflow is a dumping ground. An off-topic question is asked here, and then it gets indexed by a search engine. Other examples of the phenomenon include [Transferring files over SSH](http://stackoverflow.com/q/343711). Developers use SSH on occasion, too. – jww Mar 17 '17 at 13:22

5 Answers5

130

Apparently there is a HTTPS environment variable available that can be used easily. For people with the same question:

Header set Strict-Transport-Security "max-age=31536000" env=HTTPS
nielsr
  • 2,417
  • 2
  • 15
  • 16
  • 2
    what file is this setting made in? – ted.strauss Nov 30 '14 at 19:37
  • 7
    @ted.strauss it would be in `.htaccess` file. – zen May 07 '15 at 18:54
  • For some reason, this doesn't work on my server. Apache 2.4 – Andy Oct 19 '17 at 00:48
  • 21
    Didn't work for me as well; however appending `"expr=%{HTTPS} == 'on'"`worked. The whole line then is `Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" "expr=%{HTTPS} == 'on'"`. Maybe it's because the `always`keyword? – Adrian Föder Nov 16 '17 at 22:31
  • 6
    Note that the `HTTPS` _env var_ in the condition `env=HTTPS` is different to the _server var_ that we commonly see in mod_rewrite conditions (and Apache expr) as `%{HTTPS}`. Since this is an _env_ variable, it is dependent on the environment ie. server config. Sometimes it is never set. Sometimes it is assigned the same value as the `HTTPS` server var (so it is always set to either "off" or "on" - and the condition `env=HTTPS` is _always_ true! `env=HTTPS` simply tests whether the env var is set, not that it is "on".) Using an Apache expr as @AdrianFöder suggests is recommended on Apache 2.4+ – MrWhite Jan 26 '18 at 13:01
  • Headers got overwritten somehow when I was running PHP in CGI mode, instead of Module. That's why it didn't work for me. https://stackoverflow.com/questions/32147413/apache-wont-set-headers-for-php-script/32150308#32150308 – Andy Apr 16 '18 at 22:44
  • adrian foder answer worked very well, thank you, it required the single quotes on the 'on' – pedrotester Dec 22 '19 at 21:48
  • Adrian Föder's code didnt work but nielsr worked OK on apache 2.4.43 (strange ?) – JLuc Nov 20 '21 at 14:07
33

To build on nielsr's answer, I used the following in the .htaccess to meet the secure deployment recommendations at https://hstspreload.org which will hardcode the domain into the Chrome browser. Be aware this will enforce HSTS across your subdomains, and that inclusion in the preload list cannot easily be undone, so rtfm.

<IfModule mod_headers.c>
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" env=HTTPS
</IfModule>
LJT
  • 1,250
  • 3
  • 20
  • 25
  • 8
    It's a bit more secure yes - if you only ever intend to use HTTPS across your entire domain. If not, it's incredibly dangerous - and so it's irresponsible to suggest it without warning about that. Let's say your webserver responds with this header to a request at a top level domain (example.com) - then as you are including subdomains any other subdomain served by a different webserver over http only and not https (e.g. intranet.example.com), will not work. Including the preload tag also allows this to be submitted to a preload list so it's hardcoded into web browsers and then is irreversible. – Barry Pollard Sep 18 '16 at 11:55
  • 1
    It's a few years later, so I am just remarking that your snippet is still effective as of today. – KGIII Oct 08 '20 at 19:46
12

You can use this and put it on your htaccess file to comply with https://hstspreload.org. put this in your .htaccess file.

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

RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L,E=HTTPS:1]
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" env=HTTPS

first, it will do redirection for non https to https. and redirect non www https to www https with HSTS header.

(http://example.com -> https://example.com -> https://www.example.com - with HSTS header)

Tested and comply with https://hstspreload.org

Yuda Prawira
  • 12,075
  • 10
  • 46
  • 54
4

For httpd.conf (if you have access to edit this) you can use

<VirtualHost 65.81.122.43:443>
    Header always set Strict-Transport-Security "max-age=31536000; includeSubdomains;"
</VirtualHost>

NOTE : You need to set it on the HTTPS vhost only and cannot be on http vhost.

When should I, and should I not use .htaccess files?

Allowing .htaccess files will make Apache look for them upon every access to your server. Since parent directories are searched as well, this will take some (small) amount of time, and can impact your server's performance. Source

Aniket Thakur
  • 66,731
  • 38
  • 279
  • 289
  • Apache will always look for .htaccess files. So unless you have disabled .htaccess lookups this will not improve performance, as Apache will check for the file anyway. – Rudy Broersma Jan 31 '18 at 12:51
1

Yet another alternative is to always set the header and conditionally remove it for non-ssl connections:

Header always set   Strict-Transport-Security "max-age=31536000" early
Header        unset Strict-Transport-Security env=!HTTPS

This has the advantage, that the Header directive can be used with both the env condition as well as the early flag. With a single Header directive, env and early cannot be used together, they are mutually exclusive (see official documentation: https://httpd.apache.org/docs/current/mod/mod_headers.html#header).

studersi
  • 1,345
  • 1
  • 12
  • 14