Skip to main content
Back to blog
March 11, 2026|8 min read|Hardening Guides

HSTS (HTTP Strict Transport Security): The Complete Setup Guide

A complete guide to HTTP Strict Transport Security (HSTS). Learn what it does, how to enable it, and avoid the common pitfalls that break your site.

ZeriFlow Team

1,231 words

What Is HSTS?

HTTP Strict Transport Security (HSTS) is a security mechanism that forces web browsers to only communicate with your website over HTTPS. Once a browser receives the HSTS header from your site, it will automatically convert all future HTTP requests to HTTPS — before even making a network request.

This eliminates an entire class of attacks known as SSL stripping, where an attacker downgrades a secure connection to plain HTTP and intercepts the traffic.

Without HSTS, here is what happens when someone types yoursite.com into their browser:

  1. 1Browser sends an HTTP request to http://yoursite.com
  2. 2Your server responds with a 301 redirect to https://yoursite.com
  3. 3Browser follows the redirect to HTTPS

The problem is step 1. That initial HTTP request travels in plaintext. An attacker on the same network (a coffee shop, airport, or compromised router) can intercept it and serve a fake version of your site — or simply read everything in transit.

With HSTS, the browser skips step 1 entirely. It knows your site only speaks HTTPS and upgrades the connection internally.

How HSTS Works

HSTS operates through a single HTTP response header:

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

When a browser receives this header over a valid HTTPS connection, it stores a record that says: "For the next max-age seconds, always use HTTPS for this domain."

The HSTS lifecycle

  1. 1First visit — User visits your site over HTTPS and receives the HSTS header
  2. 2Browser stores the policy — The domain is added to the browser's HSTS list
  3. 3Subsequent visits — Browser automatically upgrades HTTP to HTTPS (307 Internal Redirect)
  4. 4Expiry — After max-age seconds without a revisit, the policy expires

Key point: HSTS only activates after the first successful HTTPS visit. It cannot protect the very first connection — that is where HSTS preloading comes in (covered below).

The Strict-Transport-Security Header Explained

The header has three directives:

max-age (required)

The time in seconds that the browser should remember to only use HTTPS. Common values:

ValueDurationUse case
3005 minutesTesting — start here
864001 dayInitial rollout
259200030 daysAfter verifying no issues
315360001 yearProduction standard
630720002 yearsRequired for preloading

Start with a low max-age (300 seconds) and increase it gradually. If you misconfigure HSTS with a long max-age, visitors will be unable to access your site over HTTP for the full duration — even if you remove the header.

Applies the HSTS policy to all subdomains. Without it, app.yoursite.com or api.yoursite.com would still be vulnerable to downgrade attacks.

Warning: Only enable this if ALL subdomains support HTTPS. If legacy.yoursite.com does not have a valid certificate, it will become inaccessible.

preload (optional)

Signals to browser vendors that you want your domain added to the HSTS preload list — a hardcoded list of HTTPS-only domains shipped with browsers. This eliminates the "first visit" vulnerability.

Setting Up HSTS

Nginx

Add the header to your server block:

nginx
server {
    listen 443 ssl http2;
    server_name yoursite.com;

    # HSTS — start with 5 minutes for testing
    add_header Strict-Transport-Security "max-age=300; includeSubDomains" always;

    # After testing, increase to 1 year:
    # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    ssl_certificate /etc/ssl/certs/yoursite.crt;
    ssl_certificate_key /etc/ssl/private/yoursite.key;

    # ... rest of your config
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name yoursite.com;
    return 301 https://$host$request_uri;
}

The always keyword ensures the header is sent even on error responses (404, 500, etc.).

Test and reload:

bash
nginx -t && systemctl reload nginx

Apache

Enable mod_headers and add the header:

apache
<VirtualHost *:443>
    ServerName yoursite.com

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

    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/yoursite.crt
    SSLCertificateKeyFile /etc/ssl/private/yoursite.key
</VirtualHost>

# Redirect HTTP to HTTPS
<VirtualHost *:80>
    ServerName yoursite.com
    Redirect permanent / https://yoursite.com/
</VirtualHost>

Enable the module and restart:

bash
a2enmod headers
systemctl restart apache2

Cloudflare

Cloudflare supports HSTS natively:

  1. 1Go to SSL/TLSEdge Certificates
  2. 2Scroll to HTTP Strict Transport Security (HSTS)
  3. 3Click Enable HSTS
  4. 4Configure: max-age = 12 months, includeSubDomains = On, Preload = On
  5. 5Click Save

Cloudflare adds the header at the edge, so you do not need to modify your origin server.

Next.js

Add the header in next.config.js:

javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'Strict-Transport-Security',
            value: 'max-age=31536000; includeSubDomains; preload',
          },
        ],
      },
    ];
  },
};

module.exports = nextConfig;

If you deploy on Vercel, the header is applied at the edge. For self-hosted Next.js, ensure your reverse proxy also handles HTTPS termination.

HSTS Preloading

The HSTS preload list is maintained at hstspreload.org. Once your domain is on this list, browsers will enforce HTTPS from the very first visit — no initial HTTP request needed.

Requirements for preloading

1. Serve a valid SSL certificate 2. Redirect all HTTP traffic to HTTPS 3. Serve the HSTS header on the base domain with: - max-age of at least 2 years (63072000 seconds) - includeSubDomains directive - preload directive 4. All subdomains must support HTTPS

How to submit

  1. 1Verify your site meets all requirements
  2. 2Go to hstspreload.org
  3. 3Enter your domain and submit

Warning: Preloading is effectively permanent. Removing your domain from the preload list takes months and requires a browser update cycle. Only preload when you are fully committed to HTTPS-only.

Common Mistakes

1. Setting HSTS on HTTP responses

HSTS headers sent over HTTP are ignored by browsers (by design — an attacker could inject the header on an insecure connection). Only set the header on HTTPS responses.

2. Starting with a long max-age

If something breaks (a subdomain without HTTPS, a misconfigured certificate), users will be locked out for the entire max-age duration. Start with 5 minutes, then 1 day, then 30 days, then 1 year.

3. Enabling includeSubDomains without checking all subdomains

Every subdomain must have a valid HTTPS configuration. Audit all of them before enabling this directive:

bash
# Check subdomains
curl -sI https://www.yoursite.com | grep -i strict
curl -sI https://app.yoursite.com | grep -i strict
curl -sI https://api.yoursite.com | grep -i strict

4. Forgetting to renew your SSL certificate

With HSTS active and your SSL certificate expired, visitors cannot access your site at all — the browser will not allow a fallback to HTTP. Set up automated certificate renewal:

bash
# Let's Encrypt auto-renewal
certbot renew --dry-run

5. Not adding the always flag in Nginx

Without always, Nginx only sends the HSTS header on 2xx and 3xx responses. Error pages will not carry the header.

Verify Your HSTS Config

Command line

bash
curl -sI https://yoursite.com | grep -i strict-transport-security

Expected output:

strict-transport-security: max-age=31536000; includeSubDomains; preload

Browser DevTools

  1. 1Open DevTools (F12)
  2. 2Go to the Network tab
  3. 3Reload the page
  4. 4Click the first request (your domain)
  5. 5Look for Strict-Transport-Security in the response headers

Automated scanning

Run a scan at zeriflow.com to check your HSTS configuration alongside all other security headers. The scanner verifies not just the presence of the header but also whether the max-age is sufficient and whether includeSubDomains is set.

A properly configured HSTS header is one of the highest-impact security improvements you can make — and it takes less than 5 minutes to set up.

Ready to check your site?

Run a free security scan in 30 seconds.

Related articles

Keep reading