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 six compliance checks. If you handle logins, payments, or personal data, a missing HSTS header is a finding any auditor will raise.
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.
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.
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Requires mod_headers. Place it in your SSL virtual host.
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.
In netlify.toml:
[[headers]]
for = "/*"
[headers.values]
Strict-Transport-Security = "max-age=31536000; includeSubDomains"
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 already serves max-age=31536000 on *.github.io. Custom domains get HSTS automatically once they're enrolled in GitHub's preload submission.
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 |
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.
www.max-age=31536000; includeSubDomains; preload.staging. included.Removal takes months of coordinated work across browsers. Only preload when your HTTPS setup is boringly reliable.
intranet.example.com dies for everyone who visited the main site first. Audit all subdomains before adding it.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 auditor-ready PDF reports covering all checks across a whole domain portfolio, see EUComply Pro ($79/year).