All articles
HSTSWeb SecurityHTTPS

Enabling HSTS the Right Way: From Header to Preload List

Learn how to set up HSTS on your website correctly — header syntax, server configs, preload submission, and the pitfalls that break sites.

WebSentry TeamMay 14, 20265 min read

HSTS (HTTP Strict Transport Security) is one of those headers that looks trivial on paper — a single line of config — but gets misconfigured constantly. A wrong max-age, a missing includeSubDomains, or a premature preload submission can lock users out of your site or leave you exposed to downgrade attacks. This post walks through enabling HSTS properly across the common server stacks, what each directive actually does, and how to verify it's working.

What HSTS Actually Does

HSTS tells browsers: for the next N seconds, only connect to this domain over HTTPS, no exceptions. Once a browser sees the header on a valid HTTPS response, it refuses to make plaintext HTTP requests to that host — even if the user types http:// or clicks an old link. This blocks SSL-stripping attacks on public Wi-Fi and protects against accidental mixed content.

The header looks like this:

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

Three directives matter:

  • max-age — How long (in seconds) the browser should remember the HTTPS-only rule. 31536000 = 1 year.
  • includeSubDomains — Applies the rule to every subdomain. Powerful, but irreversible until max-age expires.
  • preload — Signals you want the domain added to the browser-baked preload list, so HSTS applies on first visit too.

Before You Enable HSTS: A Pre-Flight Checklist

HSTS is sticky. If you push includeSubDomains with a one-year max-age and one of your subdomains is HTTP-only, you've just broken it for every returning visitor. Run through this list first:

  1. Every subdomain you care about (www, api, cdn, mail, staging, etc.) serves valid HTTPS with a trusted certificate.
  2. All HTTP traffic on those hosts already 301-redirects to HTTPS.
  3. No internal tools or admin panels rely on plain HTTP.
  4. Your TLS certificates auto-renew reliably (Let's Encrypt, ACM, etc.).
  5. You've tested with a short max-age first — say 300 seconds — before committing to a year.

Configuration by Server

Nginx

Add the header inside your HTTPS server block. Use always so it's sent on error responses too:

server {
    listen 443 ssl http2;
    server_name example.com;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}

Reload with nginx -t && nginx -s reload.

Apache

Enable mod_headers first (a2enmod headers), then add inside your HTTPS VirtualHost:

<VirtualHost *:443>
    ServerName example.com
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
</VirtualHost>

Caddy

Caddy enables HTTPS by default. Add the header in your Caddyfile:

example.com {
    header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
}

Cloudflare

If you're behind Cloudflare, set HSTS in the dashboard: SSL/TLS → Edge Certificates → HTTP Strict Transport Security. Enable it, set max-age to 12 months, toggle Include subdomains and Preload when ready. Cloudflare will inject the header for you — don't double up at the origin.

IIS

Add to web.config under system.webServer:

<httpProtocol>
  <customHeaders>
    <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains; preload" />
  </customHeaders>
</httpProtocol>

Node.js (Express)

Use Helmet, which sets sensible defaults:

const helmet = require('helmet');
app.use(helmet.hsts({
  maxAge: 31536000,
  includeSubDomains: true,
  preload: true
}));

The Rollout: Don't Go Straight to a Year

Even with the checklist passed, ramp up gradually. The recommended pattern:

  1. Week 1: max-age=300 (5 minutes), no includeSubDomains. Confirm nothing breaks.
  2. Week 2: max-age=86400 (1 day). Watch error logs and analytics for any HTTP-only resources failing.
  3. Week 3: max-age=2592000 (30 days), add includeSubDomains if all subdomains are clean.
  4. Week 4+: max-age=31536000 (1 year). Add preload only when you're confident.

Getting on the HSTS Preload List

Browsers ship a hardcoded list of domains that get HSTS treatment on the very first visit — no header request needed. To submit yours at hstspreload.org, your site must:

  • Serve a valid certificate.
  • Redirect HTTP to HTTPS on the same host.
  • Serve all subdomains over HTTPS (including www).
  • Send the HSTS header on the base domain with max-age of at least 31536000, includeSubDomains, and preload.

Think twice before submitting. Removal from the preload list takes months and rolls out only with new browser versions. If your business might ever need an HTTP subdomain, skip the preload directive.

Verifying It Works

Test with curl:

curl -I https://example.com | grep -i strict

You should see your header echoed back. For a deeper check, browsers expose the HSTS cache:

  • Chrome: chrome://net-internals/#hsts — query a domain to see its policy.
  • Firefox: Check the Network tab response headers.

For a one-shot audit across HSTS plus the rest of your security headers, CSP, TLS config, cookies and DNS, run the domain through WebSentry. It'll flag a missing includeSubDomains, a too-short max-age, or whether you're actually preload-eligible — and surface the related issues (mixed content, weak ciphers, missing Secure cookie flags) that often sit alongside an incomplete HSTS rollout.

Common Mistakes That Bite Later

  • Setting HSTS over HTTP. Browsers ignore it. The header only counts on HTTPS responses.
  • Forgetting always in Nginx. Without it, the header is skipped on 4xx/5xx responses.
  • Enabling includeSubDomains with a forgotten HTTP subdomain. Classic outage. legacy.example.com over plain HTTP suddenly stops loading.
  • Preloading too early. If you submit then realise api.example.com needs HTTP for a webhook callback, you're stuck waiting for the next browser release cycle.
  • Double headers from CDN + origin. Conflicting values cause unpredictable behaviour. Pick one place to set it.

Once HSTS is live and tested, scan your domain at websentry.dev for a free security grade — it'll confirm your HSTS policy is configured correctly and catch anything else weakening your HTTPS setup.

Check your own site

Run a free security scan and see if your site has the issues covered in this article. Results in under 30 seconds.