Contents
HSTS preload guide: how to enable HTTP Strict Transport Security without breaking your site
Table of contents
1. What is HSTS? 2. The header, explained field by field 3. How to enable it (Nginx, Apache, Cloudflare, Netlify, Vercel) 4. Choosing a max-age value safely 5. Submitting to the HSTS preload list 6. Common mistakes that take sites offline 7. Test your site now1. What is HSTS?
HTTP Strict Transport Security (HSTS) is a response header that tells browsers: "from now on, only talk to this site over HTTPS — even if the user types http:// or clicks an http link."
Without it, every visitor's first request can still travel over plain HTTP, where it can be intercepted or redirected to a lookalike site (a "SSL-stripping" attack). With HSTS set, the browser refuses to make insecure connections to your domain at all.
HSTS matters beyond security. It appears in PCI DSS guidance, in most modern security baselines, and our own scanner checks for it as part of its nine compliance checks. If you handle logins, payments, or personal data, a missing HSTS header is a finding any auditor will raise.
2. The header, explained field by field
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
| Directive | Meaning |
|---|---|
max-age=31536000 | Browsers remember "HTTPS only" for this many seconds. 31536000 = one year. |
includeSubDomains | The rule applies to every subdomain too (app., api., staging.…). Required for preload. |
preload | Says you consent to being hardcoded into browser preload lists. |
The header is ignored entirely on non-HTTPS responses — browsers only honor it over a secure connection with no certificate errors.
3. How to enable it
Nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Put it in your HTTPS server {} block. always makes sure it's sent on error responses too. Reload with nginx -s reload.
Apache
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Requires mod_headers. Place it in your SSL virtual host.
Cloudflare
Dashboard → SSL/TLS → Edge Certificates → enable "HTTP Strict Transport Security". Choose max-age, toggle includeSubDomains/preload there. Cloudflare adds the header at the edge, so no origin changes needed.
Netlify
In netlify.toml:
[[headers]]
for = "/*"
[headers.values]
Strict-Transport-Security = "max-age=31536000; includeSubDomains"
Vercel
Vercel sends max-age=63072000 automatically on all deployments. You cannot override it — but note it does not include includeSubDomains by default, so apex domains with subdomains may still want their own layer.
GitHub Pages
GitHub Pages already serves max-age=31536000 on *.github.io. Custom domains get HSTS automatically once they're enrolled in GitHub's preload submission.
4. Choosing a max-age value safely
Do not jump straight to one year. Roll out gradually so a mistake doesn't lock visitors out:
| Phase | Header | Duration |
|---|---|---|
| 1. Test | max-age=300 (5 min) | A day |
| 2. Confirm | max-age=604800 (1 week) | A week |
| 3. Commit | max-age=2592000 (1 month) | A month |
| 4. Full | max-age=31536000 + preload flags | Permanent |
5. Submitting to the HSTS preload list
The preload list is compiled into Chrome, Firefox, Safari and Edge source. Domains on it are HTTPS-only from the very first visit — even before your server has ever answered.
- Serve valid HTTPS on both the apex domain and
www. - Send the full header on all responses:
max-age=31536000; includeSubDomains; preload. - Redirect all HTTP traffic (including subdomains) to HTTPS.
- Every subdomain must serve valid HTTPS —
staging.included. - Submit at hstspreload.org.
Removal takes months of coordinated work across browsers. Only preload when your HTTPS setup is boringly reliable.
6. Common mistakes that take sites offline
- includeSubDomains with an HTTP-only subdomain. The classic outage: marketing sets the flag, and
intranet.example.comdies for everyone who visited the main site first. Audit all subdomains before adding it. - Serving the header over HTTP. Browsers ignore it there, so teams think it works when it silently doesn't. Verify on the HTTPS response specifically.
- Setting it on a redirect page only. If the header is only on the 301 handler, direct deep links never receive it.
- Duplicate conflicting headers. A CDN adds one, origin adds another with different max-age. Browsers use the first — audit which one actually ships.
- Preloading before phase 4. Preload is forever-ish. Complete the ramp-up above first.
7. Test your site now
You can verify your header in one command:
curl -sI https://yourdomain.com | grep -i strict
Or run the free EUComply scan — it checks HSTS alongside cookies, forms, legal pages, other security headers, and DORA disclosures, and tells you exactly what's missing:
For editable WordPress document starters and an HTML report from the latest scan, see EUComply Pro ($79/year per website). Runtime PDF reports and hosted monitoring are not included today.
Further reading
- The EU website compliance checklist for 2026
- GDPR cookie banner fines: what triggers them
- DORA compliance: operational resilience guide
Further reading
How does EUComply compare with the established tools? See our head-to-head comparison — pricing, features and where each one falls short.