HSTS Preloading with Nginx, Letsencrypt and Capistrano. ๐Ÿ˜Ž

HSTS Preloading with Nginx, Letsencrypt and Capistrano. ๐Ÿ˜Ž

  • bubblin
  • 2 minutes
  • October 4, 2018

Quick tip: HSTS stands for HTTPS Strict Transport Security.

HSTS is a security policy mechanism that protects against protocol downgrade attacks. In simple terms it means forcing the browser to always use https over http for your website. HSTS also protects against cookie hijacking but weโ€™ll leave that discussion out for another post. In the post below we talk about how to quickly configure your webserver (nginx) to imply HSTS on all subsequent requests and then hardcode this rule into Chrome and other major browsers to default on https for your website using HSTS preloading.

I recommend reading the following blog by Scott Helme to learn more about HSTS preloading.

โ€ฆa list of hosts that wish to enforce the use of SSL/TLS on their site is built into a browser. This list is compiled by Google and is utilised by Chrome, Firefox and Safari. These sites do not depend on the issuing of the HSTS response header to enforce the policy, instead the browser is already aware that the host requires the use of SSL/TLS before any connection or communication even takes place.

The โ€˜preloadingโ€™ part here is a way for site administrators to tell the browsers in advance that their site is https only. This way browsers can skip trying downgraded requests over insecure http and save your site some time.

HSTS directive for Nginx

Now we use the old boy Capistrano for automatic deployments and serve books only over https, so configuring nginx over to strict https with preloads was super easy. I edited the template picked up by the capistrano3-nginx gem for http โ€“> https redirection and added the following directive at the end of the file to what is now our latest nginx config template.

It is mandatory for your site to be served on https for HSTS. Weโ€™re using Certbot to install Letsencrypt with auto-renewals.

# Path to ./config/deploy/templates/nginx_conf.erb on your rails app
# Jump over to the last line!

server {
    listen 80;
    listen [::]:80 ipv6only=on;
    server_name <%= fetch(:nginx_server_name) %> www.<%= fetch(:nginx_server_name) %>;
    return 307 ^(.*) https://$host$1$request_uri permanent;

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name www.<%= fetch(:nginx_server_name) %>;
  ssl_certificate /etc/letsencrypt/live/www.bubblin.io/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/www.bubblin.io/privkey.pem; # managed by Certbot
  include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot

  # Redirection from http --> https is mandatory.
  rewrite ^ https://bubblin.io$request_uri permanent;


server {
  server_name <%= fetch(:nginx_server_name) %>;
  root <%= current_path %>/public;
  try_files $uri/index.html $uri @puma_<%= fetch(:nginx_config_name) %>;


  # Plenty of configuration here.

  # SSL is mandatory for HSTS. We're using
  # Certbot to manage Letsencrypt for us.

  listen 443 ssl http2; # managed by Certbot
  listen [::]:443 ssl http2;
  ssl_certificate /etc/letsencrypt/live/bubblin.io/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/bubblin.io/privkey.pem; # managed by Certbot
  include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot

  # Add HSTS header with preload. This is the line that does it.
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";


The HSTS directive add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"; at the bottom does it and tells the userโ€™s browser to always use https for your site.

Commit the changes and then re-deploy. Now head over to hstspreload.org.

This is where we submit our site for inclusion in Chromeโ€™s HTTP Strict Transport Security (HSTS) preload list. The list of sites that are hardcoded into Chrome as being https only. Most major browsers like Firefox, Opera, Safari, IE 11 and Edge also have HSTS preload lists based on the list compiled by Chrome. In order to be accepted to and remain on the HSTS preload list through this form, your site must satisfy the following set of requirements perpetually:

1. Serve a valid certificate.
2. Redirect from HTTP to HTTPS on the same host, if you are listening on port 80.
3. Serve all subdomains over HTTPS.
	In particular, you must support HTTPS for the www subdomain if a DNS record for that subdomain exists.
4. Serve an HSTS header on the base domain for HTTPS requests:
	i. The max-age must be at least 31536000 seconds (1 year).
	ii. The includeSubDomains directive must be specified.
	iii. The preload directive must be specified.
	iv. If you are serving an additional redirect from your HTTPS site, that redirect must still have the HSTS header (rather than the page it redirects to).

It should also be noted here that hardcoding HSTS preloading into browsers doesnโ€™t automatically mean that all aspects of security have been taken care of. It surely helps with security and plus there is a small improvement in speed and performance of your webserver because it no longer needs to determine and switch between secure and insecure protocols.

Thatโ€™s all from server side engineering folks. โค๏ธ

Iโ€™m Sonica Arora, CTO of Bubblin Superbooks and you can follow me here on Twitter.

P.S.: Did you know that Bubblin is a cool new way to read books on your iPad?

About the author

Marvin Danig

I write code with my bare hands. ๐Ÿ’ช๐Ÿป Yammer about Bubblin all day.