HSTS: how to enable it without breaking your site
HSTS forces browsers to use HTTPS only, which is great until you accidentally lock out a subdomain that doesn't have a cert. Here is the safe way to enable it.

HSTS (HTTP Strict Transport Security) is one of those headers that's both easy to enable and easy to enable wrong. The dangerous part isn't turning it on. It's that once you submit your domain to the HSTS preload list, the change is durable for months even after you remove the header, and any subdomain without HTTPS becomes unreachable in every browser that has your domain in its preload database.
Here's how HSTS actually works, what each directive does, and the step-by-step way to roll it out without locking yourself or your users out.
What HSTS does
HSTS tells every browser that visits your site: "for the next N seconds, refuse to connect to me over plain HTTP. Always upgrade to HTTPS, even if the user types http:// or follows an http:// link."
The header looks like this:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Three directives, each doing something different:
| Directive | Does |
|---|---|
| max-age=N | How long (in seconds) the browser remembers to force HTTPS |
| includeSubDomains | Applies the rule to every subdomain of this host |
| preload | Marks your domain as eligible for inclusion in the browser preload list |
When the browser sees this header, it stores the rule. From that point until the max-age expires, the browser refuses to connect to the host over HTTP. It rewrites HTTP URLs to HTTPS before sending the request and refuses to allow the user to click through certificate warnings.
The protection is real. HSTS defeats SSL-stripping attacks (where an attacker downgrades the connection to HTTP) and protects users who type the domain into a browser bar without the protocol.
The catch: HSTS is sticky
The risk in HSTS isn't the protection. It's the durability.
The browser remembers your HSTS policy for the duration of max-age. If you set max-age=31536000 (one year) and then break your HTTPS setup, every visitor who already saw your site in the last year cannot reach it. The browser refuses to fall back to HTTP. They get a security error and no clickthrough option.
It gets worse with preload. The Chromium preload list is shipped with the browser binary. Once your domain is on that list, every fresh install of Chrome, Edge, Firefox, Safari, and Opera will refuse plain HTTP to your domain even if the user has never visited it. Removing yourself from the preload list takes weeks at minimum and your removed entry stays in old browser versions for a long time after that.
If you preload, then six months later you launch a marketing campaign at legacy.yourdomain.com and that subdomain doesn't have a valid cert, every Chrome user on Earth gets a security error reaching it. No way around it except waiting for the preload removal to ship.
The safe rollout
Don't enable HSTS at the maximum settings on day one. Stage it.
Step 1: get every subdomain on HTTPS first
Before HSTS, every host you might serve has to have a valid HTTPS certificate. If www.yourdomain.com works but staging.yourdomain.com is HTTP-only and you ship includeSubDomains, staging breaks for every user who already visited the production site.
Inventory your subdomains. The easy ones: www, app, api. The dangerous ones: legacy marketing pages, vendor subdomains, internal tools, anything created years ago and forgotten.
Run a subdomain finder against your domain to surface the ones you might not know about, then check each with an SSL check to confirm it has a valid cert.
Step 2: start with a short max-age
Begin with a five-minute max-age, no includeSubDomains, no preload:
Strict-Transport-Security: max-age=300
Deploy this. Watch your logs and error tracking for 24 hours. The five-minute window means if something is wrong, the impact ends quickly when you remove the header.
Step 3: ramp the max-age slowly
If five minutes is clean, bump to one week:
Strict-Transport-Security: max-age=604800
Watch for another week. If clean, bump to one month, then six months, then one year.
The slow ramp gives you cheap rollback. Browsers honor the most recent max-age they saw. If you go from one week to one year and discover a problem on day two, you're committed for a year on the browsers that already cached the longer policy. But if you went from one week to one month and discover the problem on day two, you've only locked in a month.
Step 4: add includeSubDomains only when every subdomain is HTTPS
Once you've inventoried every subdomain and confirmed each has HTTPS, you can safely add includeSubDomains:
Strict-Transport-Security: max-age=31536000; includeSubDomains
If you're not confident in your subdomain inventory, leave this off. The cost of accidentally including a broken subdomain is that the subdomain becomes unreachable.
Step 5: preload only when you're sure
The Chrome preload list requires:
- A valid certificate
- HTTPS redirect from HTTP on the same host
- All subdomains on HTTPS
Strict-Transport-Securitywithmax-ageof at least 31536000 (one year),includeSubDomains, andpreload- Submission via hstspreload.org
Once your domain is accepted into the list, it ships in the next browser release (usually 6-12 weeks). After that, removal takes weeks or months and never affects browsers shipped with the old list embedded.
Don't preload until you've run with includeSubDomains and one-year max-age for at least a month with no issues.
The mistakes we catch most often
Across the SSL scans an AcuityScan run does, three HSTS mistakes show up repeatedly:
- HSTS without HTTPS redirect. The header tells the browser to force HTTPS, but the server still accepts plain HTTP on the first connection. The browser will upgrade on the second visit, but the first visit is unprotected. Fix: redirect all HTTP to HTTPS at the server (a 301 redirect to the
https://version), then set HSTS on the HTTPS response. includeSubDomainson a parent that breaks a subdomain. The classic:example.comships HSTS withincludeSubDomains, butmail.example.comwas running an old IMAP front-end without a cert. Email breaks for staff.- Preload submitted, then HTTPS misconfigured later. Cert expires, CDN misconfigures, someone redirects to HTTP for a debug session. The preload entry doesn't care. Users get a hard fail.
How to verify HSTS is working
Open the dev tools network panel on your site. Look at the response headers for any document request. You should see:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
The HTTP headers tool shows all the security headers for any URL, including HSTS, so you can verify the directive set without opening dev tools.
Then check the official preload status page to confirm whether your domain is currently in the Chromium preload list, queued for inclusion, or absent.
When HSTS is the wrong choice
A handful of cases where you should not preload, and might want to skip HSTS entirely:
- You serve content on a subdomain that uses self-signed certs for internal tools
- You have legacy IoT devices on a subdomain that can't be upgraded to HTTPS
- You're testing a domain you don't own long-term (don't preload domains you might let lapse)
- You're working in a corporate environment with internal-only domains routed through a proxy that does TLS inspection
For everyone else, HSTS with a one-year max-age and includeSubDomains (with or without preload) is the standard. It costs nothing, ships in one header, and protects users against an entire class of downgrade attacks.
The full security module in a full AcuityScan run checks HSTS, the preload status, and the cert chain underneath it as part of the SSL audit. If you're rolling HSTS out and want a sanity check, run a scan after each step.
Scan your own site
See what 350+ checks find on your domain.
Free, no signup, 60 seconds. Email auth · DNS · SSL · Performance · SEO · Accessibility · Privacy · Mobile.