Strict-Transport-Security
The HSTS header pins HTTPS for future visits so the browser never retries plain HTTP. What it protects against and how to deploy it safely.
Last update:
HTTP Strict Transport Security (HSTS) is a response header a site sends over
HTTPS that tells the browser to reach that host over HTTPS only, for a period
you choose. Once the browser has seen the header, it rewrites any plain
http:// address for the host to https:// before the request ever leaves
the machine, even if the user typed the insecure address or followed an old
link.
Without it, typing a bare domain into the address bar starts with a plain HTTP request, and anyone on the network path (a public Wi-Fi hotspot, a compromised router) can read or rewrite that unencrypted hop and keep the victim on an attacker-controlled HTTP version of the site. HSTS removes that opening by making the browser refuse to speak plain HTTP to your host at all.
This is the header as you would ship it after a full rollout (see how to set it up before copying it):
Strict-Transport-Security: max-age=63072000; includeSubDomains; preloadValues and what each does
| Directive | Status | Description |
|---|---|---|
max-age=<seconds> | ✅ Good | Required. How long the browser enforces HTTPS for the host. The timer refreshes on every response, so a fixed value effectively never expires for returning visitors. |
includeSubDomains | ✅ Good | Extends the policy from the host to all of its subdomains, downward only. |
preload | ✅ Good | Opts in to the browser preload list at hstspreload.org. Non-standard (not in RFC 6797) and hard to undo; submit only when the whole domain is ready. |
max-age=0 | ❌ Risky | The off switch. Tells the browser to forget the policy, including includeSubDomains. Use it only to roll back, and note it takes effect only on the next response received over valid HTTPS. |
The policy attaches to the exact host that served the header. A header on
www.example.com never covers example.com, and it covers subdomains only
when includeSubDomains is present, and only downward: a policy on
secure.example.com covers login.secure.example.com but not example.com
or sibling hosts. HSTS identifies hosts by domain name only, so it never
applies to a host addressed by IP. Browsers ignore the header entirely when
it arrives over plain HTTP.
What it protects against
- SSL stripping and downgrade attacks. An active attacker on the network can intercept a plain HTTP request and serve an HTTP version of the site, silently keeping the victim off TLS. With HSTS the browser upgrades the URL before the request goes out, so there is no insecure request to intercept.
- Cookie theft on the insecure first hop. Without the header, the first
request to a typed domain goes over plain HTTP, and any cookie without the
Secureattribute (see cookie security) rides that hop in cleartext where a passive eavesdropper can copy it. - Same-host mixed-content mistakes. URLs pointing at a known HSTS host
are rewritten, so a stray
http://image or script reference to your own host gets upgraded instead of loading insecurely. - Certificate spoofing via click-through. For an HSTS host, the browser treats any certificate error as fatal. There is no "proceed anyway" button, so an attacker cannot rely on users clicking through a warning for a forged certificate.
Risks without it
Every fresh visit starts insecure. When a user types your domain or follows
an http:// link, the browser sends the first request over plain HTTP and
waits for your redirect. That single unencrypted round trip is enough for an
attacker on the path to hijack the session: non-Secure cookies travel in
cleartext, and an active attacker can answer the request themselves and never
let the user reach HTTPS at all. A server-side redirect to HTTPS does not
close this gap, because the redirect only happens after the insecure request
has already been sent and possibly tampered with.
Risks and gotchas when using it
- The first visit is not protected. HSTS only works after the browser has received the header once over valid HTTPS. The very first request from a new browser, and the first request after the policy expires, still goes out over plain HTTP unless the domain is on the preload list.
- Preload is close to permanent. OWASP warns of permanent consequences if
a preloaded site ever needs to move back to HTTP. Removal from the list
requires dropping the
preloaddirective first, then takes 6 to 12 weeks to reach most Chrome users and longer for other browsers, and the submission requirements apply for as long as you are listed. WithincludeSubDomains, preloading also forces every subdomain onto HTTPS, including internal, HTTP-only hosts that browsers can no longer reach. Audit all nested subdomains before submitting. - Silent no-ops. The most common misconfiguration is sending the header
on plain HTTP responses, which browsers must ignore, so nothing happens and
nothing warns you. The header is also ignored on responses with certificate
errors. And because the policy binds to the exact host, serving it on
www.example.comwhile the apexexample.comsends nothing leaves the apex unprotected.
How to set it up
Ramp max-age up in stages, as hstspreload.org
recommends, so a mistake locks users out for minutes, not years:
- Serve the whole site over HTTPS, including
wwwand every subdomain you plan to cover. - Configure the HTTP listener to answer every request with a permanent (301) redirect to the HTTPS URL, without the HSTS header. RFC 6797 forbids sending it over insecure transport, and browsers ignore it there anyway.
- Add the header to all HTTPS responses with a small value first:
max-age=300; includeSubDomains(5 minutes). - Raise it to
max-age=604800(1 week), thenmax-age=2592000(1 month), waiting out each stage's fullmax-ageand watching for breakage before the next step. - Make sure the apex domain serves the header too, not just
www; each host needs to receive it directly. - When nothing has broken, move to
max-age=63072000(2 years) and, if you want first-visit protection, addpreloadand submit the domain at hstspreload.org. - Check the deployed header, and the rest of your response headers, with the security headers scanner.
Recommendation
Send Strict-Transport-Security on every HTTPS response, with a 2 year
max-age, includeSubDomains, and preload once the rollout is complete:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preloadThis is the value the
OWASP HTTP Strict Transport Security cheat sheet
recommends: a 2 year max-age with subdomain coverage and preload. Treat it
as the end state, not the starting point. Reach it through the staged ramp
above, and add preload only once every subdomain runs on HTTPS and you
accept that the decision is close to permanent.
Browser support
Baseline widely available: Chrome, Firefox, and Safari all enforce the
header, so support is effectively universal. The preload directive is not
part of RFC 6797, but the preload list it feeds is consumed by all major
browsers.
FAQ
Does HSTS protect the first visit?
No. HSTS only takes effect after the browser has received the header once over valid HTTPS, so the very first request from a new browser, and the first request after the policy expires, still go out over plain HTTP. Preload closes that gap by shipping the policy in the browser.
What is a good max-age for HSTS?
Two years (max-age=63072000) with includeSubDomains and preload, the value
the OWASP cheat sheet recommends. Do not start there. Ramp up from a small value
like max-age=300, waiting out each stage and watching for breakage, then move
to the two-year end state once every subdomain runs on HTTPS.
How do I turn off HSTS?
Send max-age=0 over HTTPS. The browser stops treating the host as a Known HSTS
Host on the next response it receives over valid HTTPS, and that clears
includeSubDomains too. If the domain is preloaded, drop the preload
directive first and use the removal form, which takes weeks to propagate.
Does HSTS cover subdomains?
Only when includeSubDomains is present, and only downward. The policy attaches
to the exact host that served the header, so a header on secure.example.com
covers login.secure.example.com but not example.com or sibling hosts.
Without includeSubDomains it covers just the one host, so the apex must send
its own header.
See also
- Security headers overview
- CSP upgrade-insecure-requests, which upgrades insecure subresources inside your pages; it complements HSTS rather than replacing it
- Cookie security
for the
Secureattribute HSTS does not replace - Deprecated headers, the TLS-era headers (HPKP, Expect-CT) that did not survive
- Security headers scanner to check your deployed headers