HSTS Explained — max-age Strategy, includeSubDomains Pitfalls, and the Preload List
HSTS — HTTP Strict Transport Security — is the header that turns "we redirect to HTTPS" into "the browser will not speak HTTP to us at all." It's one line, it closes a real attack window that redirects alone leave open, and it's near the top of every security-header audit for good reason. It is also the header with the most painful failure mode: a careless value can lock users out of parts of your infrastructure for months, with no server-side switch to undo it.
This guide covers how HSTS actually works, a safe max-age ramp-up strategy, the includeSubDomains traps, and the preload list — including why preloading is close to irreversible.
The problem HSTS solves
Redirecting HTTP to HTTPS is necessary but not sufficient. The first request a user makes to http://example.com — from typing the bare domain, an old bookmark, or any http:// link — travels in plaintext before your redirect is ever seen. An attacker in the network path (public Wi-Fi is the classic setting) can intercept that request and hold the victim on attacker-controlled plain HTTP, harvesting everything, while proxying your real site behind the scenes. The user sees your content and never notices the missing padlock.
HSTS removes that window. Once a browser has seen the header, it rewrites every http:// URL for your domain to https:// internally — the plaintext request never leaves the machine.
How it works
Serve this header over HTTPS (browsers ignore it on plain-HTTP responses, since an attacker could otherwise inject it):
Strict-Transport-Security: max-age=31536000; includeSubDomains
max-age— how long, in seconds, the browser should enforce HTTPS-only for this host. The browser refreshes the timer on every response carrying the header.includeSubDomains— extends enforcement to every subdomain of the host that set it.preload— signals consent for inclusion in the browser-shipped preload list (more below; the token alone does nothing).
Two side effects worth knowing. First, certificate errors on an HSTS host become non-bypassable — the browser removes the "proceed anyway" option. That's the point (an attacker with a bad cert shouldn't be one click from success), but it also means an expired certificate on an HSTS site is a hard outage, not a warning. Keep an eye on expiry with an SSL certificate checker. Second, HSTS state lives in the browser, not on your server — you can stop sending the header, but every browser that already saw it keeps enforcing until its stored max-age runs out.
max-age strategy: start low, ramp up
The published best practice is max-age=31536000 (one year) or more. The mistake is shipping that on day one. If anything on your domain still needs HTTP — an internal tool, a legacy vhost, a piece of hardware with an HTTP-only admin panel — you've broken it for a year, and clearing your server config won't un-break visitors' browsers.
Ramp instead:
max-age=300(5 minutes) — deploy, then browse everything: app, admin, APIs, webhook receivers. Any breakage self-heals in minutes once you drop the header.max-age=86400(1 day) — run for a week or two. Watch logs and support channels.max-age=2592000(30 days) — run for a month. This is where quiet corner-case traffic surfaces.max-age=31536000(1 year) — the destination value.
Ramp includeSubDomains with the same caution — ideally adding it at step 2 or 3 once you've audited subdomains, not at step 4 when the timer is already long.
The includeSubDomains pitfalls
includeSubDomains is where HSTS incidents actually happen, because it applies to hosts you've forgotten exist:
- Legacy internal hosts.
intranet.example.comorprinter.example.comserving plain HTTP become unreachable in any browser that visited your main site. - Third-party CNAMEs. Subdomains delegated to external services (status pages, marketing tools, tracking domains) must all present valid TLS for your users.
- Certificate coverage. Every subdomain now needs a valid certificate — no exceptions, and remember the errors are non-bypassable.
Before enabling it, enumerate your DNS zone and check every name that resolves. If you can't confidently list your subdomains, you're not ready for includeSubDomains — and definitely not for preload.
The preload list — powerful and nearly irreversible
Even with HSTS, one gap remains: the very first visit from a browser that has never seen your header. Preloading closes it. The HSTS preload list is compiled into Chrome and consumed by other major browsers, marking your domain HTTPS-only before any request is ever made.
Submission (via hstspreload.org) requires, as of early 2026: a valid certificate, HTTP redirecting to HTTPS on the same host, all subdomains served over HTTPS, and an HSTS header on the HTTPS root with max-age of at least one year, includeSubDomains, and preload:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Here's the part to take seriously: preloading is effectively permanent. Removal is possible but slow — the change has to propagate through browser releases, which takes months, and every browser installation that hasn't updated keeps enforcing indefinitely. The list maintainers explicitly warn to be sure before submitting. If any subdomain, ever, needs plain HTTP — including ones a future acquisition or side project might create — preload will break it with no timely undo. Preload the domains you're certain about; don't preload "because the checklist said so."
Don't put preload in your header casually, either: it declares consent, and third parties can submit any domain whose header qualifies.
Copy-paste configs
Start with the ramp value, not the final one. nginx (in the HTTPS server block only):
add_header Strict-Transport-Security "max-age=86400; includeSubDomains" always;
Apache (requires mod_headers):
Header always set Strict-Transport-Security "max-age=86400; includeSubDomains"
Caddy:
header Strict-Transport-Security "max-age=86400; includeSubDomains"
And make sure plain HTTP still redirects to HTTPS with a 301 — HSTS complements the redirect; it doesn't replace it:
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
If you need to back out
Set max-age=0 on the HTTPS site. Browsers that revisit will drop their stored policy immediately — but browsers that don't revisit keep enforcing until their old timer expires. That asymmetry is the whole argument for ramping slowly: your worst-case rollback time equals your current max-age. (And if you're preloaded, rollback means the months-long removal process.)
Verify it, then keep verifying it
HSTS earns its place near the top of the grading criteria in any header audit — it's one of the checks behind our A+–F security grade, alongside CSP, frame protection and the rest of the headers covered in our complete security-headers guide. And because HSTS makes certificate errors non-bypassable, cert expiry and header presence are two halves of the same risk.
CompleteStatus monitors both: continuous security-header grading with copy-paste remediation, and certificate expiry alerts at 30/14/7 days — in the same dashboard as your uptime. Check your current headers with the free security header checker, then start monitoring free. The free tier allows commercial use.