HTTP Strict Transport Security (Apache – NGINX – Lighttp)

To totally unlock this section you need to Log-in


HTTP Strict Transport Security (often abbreviated as HSTS) is a security feature that lets a web site tell browsers that it should only be communicated with using HTTPS, instead of using HTTP. This tutorial will show you how to set up HSTS in Apache2, NGINX and Lighttpd. It is tested with all mentioned webservers, NGINX 1.1.19, Lighttpd 1.4.28 and Apache 2.2.22 on Ubuntu 12.04, Debian 6 & 7 and CentOS 6. It should work on other distro’s however, these are just reference values.

What is HTTP Strict Transport Security?

Quoting the Mozilla Developer Network:

If a web site accepts a connection through HTTP and redirects to HTTPS, the user in this case may initially talk to the non-encrypted version of the site before being redirected, if, for example, the user types or even just

This opens up the potential for a man-in-the-middle attack, where the redirect could be exploited to direct a user to a malicious site instead of the secure version of the original page.

The HTTP Strict Transport Security feature lets a web site inform the browser that it should never load the site using HTTP, and should automatically convert all attempts to access the site using HTTP to HTTPS requests instead.

An example scenario

You log into a free WiFi access point at an airport and start surfing the web, visiting your online banking service to check your balance and pay a couple of bills. Unfortunately, the access point you’re using is actually a hacker’s laptop, and they’re intercepting your original HTTP request and redirecting you to a clone of your bank’s site instead of the real thing. Now your private data is exposed to the hacker.

Strict Transport Security resolves this problem; as long as you’ve accessed your bank’s web site once using HTTPS, and the bank’s web site uses Strict Transport Security, your browser will know to automatically use only HTTPS, which prevents hackers from performing this sort of man-in-the-middle attack.

Do note that HSTS does not work if you’ve never visited the website before. A website needs to tell you it is HTTPS only.

Important regarding preload

Take note that, when connecting to an HSTS host (a public web site, usually) for the first time, the browser won’t know whether or not to use a secure connection, because it has never received an HSTS header from that host. Consequently, an active network attacker could prevent the browser from ever connecting securely (and even worse, the user may never realize something is amiss). To mitigate this attack, Google Chrome, and then Safari and Firefox, have added a list of hosts that want HSTS enforced by default, called usually HSTS preload lists.

In the below configuration the preload directive was used.

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

If the site owner would like their domain to be included in the HSTS preload list maintained by Chrome (and used by Firefox and Safari), then use the header above.

Please note that that THE PRELOAD DIRECTIVE WILL HAVE SEMI-PERMANENT CONSEQUENCE. If you are testing, screw up or don’t want to use HSTS anymore you might be on the preload list.

It is important that you understand what you are doing and that you understand that the preload directive means that it will end up in browsers. If your HTTPS configuration is wrong, broken or you don’t want to use HTTPS anymore, you will experience problems.

If you still want to use preload, just append it to the header after the semi-colon.

Set up HSTS in Apache2

Edit your apache configuration file (/etc/apache2/sites-enabled/website.conf and /etc/apache2/httpd.conf for example) and add the following to your VirtualHost:

# Optionally load the headers module:
LoadModule headers_module modules/

Header always set Strict-Transport-Security “max-age=63072000; includeSubdomains;”

Now your website will set the header every time someone visits, with an expiration date of two years (in seconds). It sets it at every visit. So tomorrow, it will say two years again. You do have to set it on the HTTPS vhost only. It cannot be in the http vhost.

To redirect your visitors to the HTTPS version of your website, use the following configuration:

<virtualhost *:80>
Redirect permanent /

If you only redirect, you dont even need a document root.

You can also use mod_rewrite, however the above method is simpler and safer. However, mod_rewrite below redirects the user to the page they were visiting over https, the above config just redirects to /:

<virtualhost *:80>
<ifmodule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

And don’t forget to restart Apache daemon.


The lighttpd variant is just as simple. Add it to your Lighttpd configuration file (/etc/lighttpd/lighttpd.conf for example):

server.modules += ( “mod_setenv” )
$HTTP[“scheme”] == “https” {
setenv.add-response-header = ( “Strict-Transport-Security” => “max-age=63072000; includeSubdomains; “)

And restart Lighttpd. Here the time is also two years.


NGINX is even shorter with its config. Add this in the server block for your HTTPS configuration:

add_header Strict-Transport-Security “max-age=63072000; includeSubdomains; “;

Don’t forget to restart NGINX.

Some sites run HTTP and HTTPS versions of a website within the same NGINX or NGINX Plus server, to make its content accessible through either protocol:

server {
listen 80;
listen 443 ssl;
# …

This is not appropriate when using HSTS because you don’t want users to access content over HTTP. Instead, you want to redirect all HTTP website accesses to use HTTPS:

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;

# Discourage deep links by using a permanent redirect to home page of HTTPS site
return 301 https://$host;

# Alternatively, redirect all HTTP links to the matching HTTPS page
# return 301 https://$host$request_uri;

server {
listen 443 ssl;

add_header Strict-Transport-Security “max-age=31536000; includeSubDomains” always;

X-Frame-Options header

The last tip we’ll give you is the X-Frame-Options header, which you can add to your HTTPS website to make sure it is not embedded in a frame or iframe. This avoids clickjacking, and might be helpfull for HTTPS websites. Quoting the Mozilla Developer Network again:

The X-Frame-Options HTTP response header can be used to indicate whether or not a browser should be allowed to render a page in a <frame /> or <iframe>. Sites can use this to avoid clickjacking attacks, by ensuring that their content is not embedded into other sites.

You can change DENY to SAMEORIGIN or ALLOW-FROM uri, see the Mozilla link above for more information on that. (Or the RFC.)

X-Frame-Options for Apache2

As above, add this to the apache config file:

Header always set X-Frame-Options DENY


This goes in the lighttpd config. Make sure you don’t double the above set config, if you have that, just add the rule it to it.

server.modules += ( “mod_setenv” )
$HTTP[“scheme”] == “https” {
setenv.add-response-header = ( “X-Frame-Options” => “DENY”)


Yet again, in a server block:

add_header X-Frame-Options “DENY”;

Testing HTTP Strict Transport Security Deeply

Once a client is presented with the HSTS policy, it caches the information for the specified max-age period. During that period, the browser refuses to access the web service over unencrypted HTTP, and refuses to grant exceptions to certificate errors (if the site previously presented a valid, trusted certificate).

If you specify the includeSubDomains parameter for an HSTS policy, these restrictions also apply to all subdomains of the current domain.

It’s very difficult to back out an HSTS policy in order to remove the HTTPS version of a website or service. When you test HSTS, use a very short max-age timeout and ensure you’re comfortable with the effects and the obligation to maintain an HTTPS version of your site.

When you first go live with your HSTS policy, keep max-age small and increase it only when you’re confident about doing so.


1 thought on “HTTP Strict Transport Security (Apache – NGINX – Lighttp)”

Comments are closed.