CentralCSP
Security headers

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; preload

Values and what each does

DirectiveStatusDescription
max-age=<seconds>✅ GoodRequired. 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✅ GoodExtends the policy from the host to all of its subdomains, downward only.
preload✅ GoodOpts 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❌ RiskyThe 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 Secure attribute (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 preload directive 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. With includeSubDomains, 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.com while the apex example.com sends 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:

  1. Serve the whole site over HTTPS, including www and every subdomain you plan to cover.
  2. 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.
  3. Add the header to all HTTPS responses with a small value first: max-age=300; includeSubDomains (5 minutes).
  4. Raise it to max-age=604800 (1 week), then max-age=2592000 (1 month), waiting out each stage's full max-age and watching for breakage before the next step.
  5. Make sure the apex domain serves the header too, not just www; each host needs to receive it directly.
  6. When nothing has broken, move to max-age=63072000 (2 years) and, if you want first-visit protection, add preload and submit the domain at hstspreload.org.
  7. 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; preload

This 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

Sources

On this page