What HSTS actually fixes
When a user types example.com, the browser does not know whether you speak HTTPS or HTTP. By default it tries port 80 (HTTP). Your server replies with a 301 redirect to HTTPS. That first plain request is the problem.
In the milliseconds before the redirect arrives, anyone on the same network (a coffee-shop Wi-Fi, a hotel router, a compromised ISP node) can intercept it and reply with a fake page over HTTP. The user sees the site, the URL bar reads http://, and the attacker proxies real traffic in the middle. This is SSL stripping. The padlock never shows up, but most people do not check.
HSTS is one HTTP response header that tells the browser: “from now on, never speak HTTP to this domain”. The browser stores that rule locally. Next time the user types example.com, the browser silently rewrites it to https://example.com before the request leaves the machine. There is no plain HTTP request for the attacker to intercept.
You can inspect the stored rules in Chrome at chrome://net-internals/#hsts.
A first-visit caveat
HSTS only works after the browser has seen the header at least once. The very first visit, on a brand new device, still goes through HTTP and the redirect. That window is small, but it exists.
The fix for that window is the Preload list (covered below).
The header, line by line
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
max-ageis the time in seconds the browser keeps the rule. 63072000 is two years.includeSubDomainsextends the rule to every subdomain. Powerful and easy to misuse.preloadis your declaration of intent to be added to the browser preload list. It does not add you automatically.
Roll it out without locking yourself out
The single most common HSTS mistake is shipping max-age=63072000; includeSubDomains on day one and discovering, three hours later, that the internal billing dashboard at billing.example.com has no SSL certificate. Browsers that already cached the rule will refuse to load it. You cannot fix this from the server. The browser will hold the rule until it expires.
Roll it out in stages:
- Start with
max-age=300(five minutes). NoincludeSubDomainsyet. - Audit every subdomain. Internal tools, log servers, staging, that one printer. Make sure HTTPS works on all of them.
- After a week with no reports, raise
max-ageto one week, then one month, then a year. - Once you are confident every subdomain serves HTTPS cleanly, add
includeSubDomains. - Only then add
preloadand submit to the list.
If something breaks at step 1 or 2, you can fix it before any user has cached a long rule.
Server configuration
Nginx
add_header Strict-Transport-Security "max-age=300" always;
The always flag matters. Without it, Nginx does not send the header on error responses (500, 502, 504). If a user hits an error on a hostile network and their HSTS rule has expired, they end up unprotected at the worst moment.
When you are ready to graduate:
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
Apache
In your virtual host or .htaccess:
Header always set Strict-Transport-Security "max-age=300"
The mod_headers module must be enabled. On shared hosting it usually is. If it is not, this line in .htaccess will trigger a 500 error on every request, so test in staging first.
Cloudflare
In the dashboard: SSL/TLS, Edge Certificates, HSTS. Toggle it on, choose your max-age, choose whether to include subdomains.
The footgun: enabling includeSubDomains here is irreversible from the user’s perspective. If a subdomain bypasses the orange cloud and has no valid certificate, that subdomain becomes unreachable for every user whose browser cached the rule. Cloudflare gives you a one-click switch and the consequences are yours.
Verify it works
Trust what the browser receives, not what the config says.
curl -I https://example.com | grep -i strict
You should see Strict-Transport-Security: max-age=....
In the browser, open DevTools, the Network tab, click the HTML document, look in Response Headers. The header should be there.
For deeper inspection, chrome://net-internals/#hsts lets you query a specific domain and see the cached rule, expiry, and whether includeSubDomains is on.
The Preload list
The preload list is a hardcoded list of domains shipped inside Chrome, Firefox, Safari, and Edge. Domains on the list get the HTTPS rule applied on the first ever visit, before any header is received. This closes the first-visit window.
Requirements:
- Valid SSL certificate on the apex and
www. - Port 80 redirects to HTTPS on the same host.
- All subdomains served over HTTPS.
- The header serves
max-ageof at least one year, plusincludeSubDomainsandpreload.
Submit at hstspreload.org. Removal is slow (months, sometimes longer), so only submit when you are sure.
A note on regulators
HSTS is treated by EU data protection authorities as part of the “appropriate technical measures” under Article 32 GDPR for sites handling personal data. Recent enforcement actions have cited missing transport security as a contributing factor (Carrefour was fined three million euros in 2020 for a mix of issues including weak transport-layer protections). This is not the entire reason to deploy HSTS, but if a regulator audits you, having it configured correctly is a clean answer.
Performance side effect
Once HSTS is cached, the browser skips the HTTP-to-HTTPS redirect entirely. On mobile networks that saves roughly 200 to 500 ms on the first request to your domain. It also stops Google from indexing the HTTP version of your URLs, which avoids duplicate-content edge cases.
For a more complete header setup, see our guide to Content Security Policy and the post on clickjacking and X-Frame-Options. The SEO Expert App checks for missing security headers automatically.