Configure HSTS (HTTP Strict Transport Security) for Apache and Nginx

HTTP Strict Transport Security (or HSTS) is a security capability to force web clients using HTTPS. The idea behind HSTS is that clients which always should communicate as safely as possible. At achieve this, the web server and web browser will prefer the HTTPS protocol instead of HTTP.

Benefits

The clear benefit of “forcing” a client to use HTTPS directly, is decreasing the risk of sharing any sensitive information via a protocol which can be snooped upon. Additionally it improves the performance by eliminating one redirect response (301/302). Another benefit is to force using a secure connection and deny a client if this can not be guaranteed (e.g. expired or self-signed certificate).

Screenshot of HTTPS with HTST, HPKP and forward secrecy

HTTPS configured with HTST, HPKP and forward secrecy.

Configuration

Configure HSTS on Apache

Load the headers and mod_rewrite module (just to be sure)

# Load modules (or use the IfModule)  
LoadModule headers_module modules/mod_headers.so
LoadModule rewrite_module modules/mod_rewrite.so

Rewrite HTTP connections and redirect them to HTTPS:

# Redirect HTTP connections to HTTPS
<IfModule mod_rewrite.c>  
  RewriteEngine On  
  RewriteCond %{HTTPS} off  
  RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]  
</IfModule>

Now configure the virtual host:

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

Configure HSTS on Nginx

To use HSTS on Nginx, use the add_header directive in the configuration. Then tell clients to use HSTS with a specific age.

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
  • max-age: expire time
  • includeSubDomains: also apply this protection on our subdomains
  • preload (optional): preload list in some browsers, requires a max-age with a minimum value of 31536000

The always statement at the end of the add_header setting ensures that the header is always inserted in the response headers.

Adjust the related virtual hosts to perform a redirect (301) to the secured version of the website:

http {

    server {
        listen 80;
        server_name example.com
        return 301 https://$server_name$request_uri;
    }

    server {
        listen 443;
        server_name example.com
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    }

}

Important notes

The HSTS header should only be sent over a secured channel, therefore HTTP responses should not include them.

Within the headers, the max-age defines what period the site is willing to accept HTTPS-only (31536000 in the examples are 12 months). Usually, the amount of time is less important. This is because the trend is to keep using HTTPS for privacy and data protection anyways.

Additionally, make sure the domain itself is also properly configured for HSTS. This reduces attacks on the underlying subdomain names.

Technical details

RFC: RFC6797 (HTTP Strict Transport Security (HSTS))

More resources

See also the Wikipedia page on HTTP Strict Transport Security.

Feedback

Small picture of Michael Boelen

This article has been written by our Linux security expert Michael Boelen. With focus on creating high-quality articles and relevant examples, he wants to improve the field of Linux security. No more web full of copy-pasted blog posts.

Discovered outdated information or have a question? Share your thoughts. Thanks for your contribution!

Mastodon icon