HSTS Preload: What It Actually Costs You Before You Flip the Switch
A few years back, security educator Tanya Janca registered a .dev domain, pointed it at a simple registrar forwarding rule, and got HTTPS errors in front of a live audience the first time she shared the link. Nothing was wrong with her site. The problem was that every .dev domain ships HSTS preload at the TLD level, whether the owner asked for it or not, and a plain forward isn't HTTPS. Her browser refused to even try the insecure connection.
That's the thing about HSTS preload that most quick guides skip past: it isn't a header you toggle and walk away from. It's a commitment baked into browser code that ships to hundreds of millions of users, and getting off it later is measured in weeks, not minutes. If you already send a Strict-Transport-Security header (maybe you built one with this site's header generator, after reading what HSTS actually does) and you're staring at the preload directive wondering whether to add it, this article is the pre-flight check before you do.
What the HSTS Preload List Actually Is
The preload list is a static file, compiled directly into the Chromium browser binary. Firefox, Safari, Edge and Opera all pull from that same Chromium-maintained list rather than running their own, according to Chromium's own documentation. That detail matters more than it sounds like it should.
Because the list ships inside the browser, there's no live lookup happening when someone visits your site. It's not DNS. It's not a certificate check performed on the fly.
Your domain is either baked into that release of the browser or it isn't, and getting added or removed only happens on the browser's release schedule, not on your schedule. That's the root cause behind everything painful about this feature, including the removal timeline further down this page.
It's also worth being precise about where preload sits, technically. MDN notes plainly that the preload directive "is not part of the HSTS specification" defined in RFC 6797. RFC 6797 covers the base Strict-Transport-Security header, the one that tells a browser "remember to use HTTPS for this host next time."
Preload is a separate mechanism that Google, as the Chromium maintainer, layered on top of it and other vendors adopted by convention, not by standard. If you want the fundamentals of the header itself, header syntax, max-age behavior, and how HSTS differs from a simple redirect, this site covers that ground in the headers breakdown post. This article assumes you already have that piece in place and picks up from there.
The eligibility checklist Chrome actually checks
Before hstspreload.org will even accept a submission, four conditions have to be true simultaneously. Miss one and the form rejects you outright:
- A valid TLS certificate on the domain, no expired or self-signed certs.
- An HTTP-to-HTTPS redirect on the same host, served on port 80, if you're serving one at all.
- Every subdomain reachable over HTTPS, with no exceptions carved out.
- A Strict-Transport-Security header present on the HTTPS response (and on the redirect, if there is one) with three specific pieces:
max-ageof at least 31536000,includeSubDomains, andpreloaditself.
Source it yourself at hstspreload.org, the official submission tool Google and the Chromium project run. It's the canonical reference here, and it's worth reading in full before you submit anything, not just skimming a summary of it (this article included).
HSTS max-age best practice: why 31536000 is the floor, not the target
31536000 seconds is one year. That's the documented minimum hstspreload.org will accept, not a recommendation. The example header hstspreload.org itself publishes uses double that:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Sixty-three million and change seconds works out to two years, and OWASP's HTTP Strict Transport Security Cheat Sheet recommends that same two-year figure as production practice, not the bare one-year floor. OWASP's language on this is unusually blunt for a cheat sheet: sending preload "can have PERMANENT CONSEQUENCES and prevent users from accessing your site and any of its subdomains if you find you need to switch back to HTTP." Capital letters, theirs. When a standards body writes in caps, it's worth reading twice.
The practical answer for hsts max age best practice: start low while testing, raise it in stages, and only land on a year or two once you're certain nothing on your domain still depends on plain HTTP. More on that staging sequence below.
The includeSubDomains trap (and why Tanya Janca's story isn't rare)
Here's where most preload regret actually comes from. The includeSubDomains directive extends HTTPS-only enforcement to every subdomain of your registered domain, and preload eligibility requires it with zero exceptions. There's no "except for legacy.example.com" option. None.
The people who run hstspreload.org describe this pattern in their own words: "We get regular emails from site operators who...find themselves on the preload list without realizing that some subdomains cannot support HTTPS. Removal tends to be slow and painful for those sites." That's not a hypothetical risk. That's the maintainers describing their actual support inbox.
Tanya Janca's .dev domain story is really the same failure mode wearing a different hat. She didn't choose preload voluntarily, Google's registry mandates it for the entire .dev and .app TLDs, but the failure was identical: a host under her domain (the forwarding setup) couldn't serve HTTPS, and the browser simply refused the connection rather than falling back to HTTP. If you're on .dev or .app and haven't audited every subdomain and forwarding rule for HTTPS support, that's not a someday task. It's already live.
A staged rollout: how to reach preload without breaking anything
Security researcher Scott Helme publishes a staged approach in his HSTS Cheat Sheet that treats max-age as a dial you turn up gradually rather than a switch you flip once. It's practitioner guidance, not an official spec, but it maps closely to how experienced teams actually roll this out. Here's the sequence, adapted:
| Stage | Header | What you're checking for |
|---|---|---|
| 1. Test | max-age=600 | HSTS behaves correctly, no unexpected lockouts, easy to undo (10 minutes) |
| 2. Add subdomains | max-age=600; includeSubDomains | Every subdomain still resolves and serves valid HTTPS |
| 3. Scale up | max-age=31536000; includeSubDomains | Full year in production, stable for weeks with no HTTP-only exceptions surfacing |
| 4. Preload | max-age=31536000; includeSubDomains; preload | Only after stage 3 has held without incident |
Notice that preload is the last thing added, not the first. That ordering is deliberate. Everything before it is reversible within minutes by changing a header value. Once you add preload and submit, you've traded a config change for a multi-week browser release cycle.
Setting it on Apache
If you're serving this from an Apache host, the header goes through mod_headers, and it needs to be present on every response, including the initial HTTP-to-HTTPS redirect if you have one. Here's stage 4, the final production line:
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
</IfModule>
The <IfModule> wrapper stops the whole .htaccess file from failing if mod_headers isn't enabled on that server, a common shared-hosting gap. Header always set sends the header on every response code, not just 200s, which matters because error pages and redirects need HSTS too if you want the eligibility check to pass. Drop the always keyword and you'll find the header missing on 3xx and 4xx responses, which is exactly the kind of gap hstspreload.org's automated crawler will catch and reject you for. For the full range of Apache rewrite and redirect rules that pair with this (the actual HTTP-to-HTTPS redirect block preload requires), htaccess.tools covers the rule syntax in more depth than fits here.
How to verify it worked
Run the header check yourself before you touch the submission form. This site's own security headers checker fetches your live response and flags exactly the issues hstspreload.org cares about: missing max-age, a max-age under the one-year floor, preload present without a qualifying max-age (a contradiction the generator catches automatically), or includeSubDomains missing entirely. HSTS is only one line in that report; if you haven't set CSP, X-Frame-Options or the rest of the header set yet, the site's breakdown of every header covers what to add next.
If you'd rather check from a terminal, curl -I https://yourdomain.com and look for the Strict-Transport-Security line in the response headers. A malformed header is the single most common reason preload submissions get rejected or silently fail to register, so confirm it's clean before you go near the form.
Submitting to the HSTS preload list
Once your header has held at max-age=31536000 (or higher) with includeSubDomains for a stability window, and every subdomain has been confirmed on valid HTTPS, submission itself is a short form at hstspreload.org: enter your domain, the tool runs its own live check against your eligibility criteria, and if you pass, your domain moves into a "pending" state.
Pending isn't preloaded yet. Chromium compiles the list into new browser builds on its own release cadence, so there's a gap, sometimes weeks, between "pending" and your domain actually enforcing HSTS from first connection for users running an updated browser. That's a one-way door opening slowly, and it's worth sitting with that fact before you click submit, not after.
Getting off the list: why removal takes 6-12 weeks
Removal exists, but it's not instant and it's not guaranteed to feel fast. Three conditions have to be true at once, per hstspreload.org's removal page: the domain must still be preloaded or pending, it must still serve valid HTTPS, and it must send a valid HSTS header that no longer contains the preload token. Removing the header entirely does not count. You remove the directive, not the whole header.
Then you wait. The removal page states it directly: "a preload list domain removal may take 6-12 weeks to reach most Chrome users, and may take longer for other browsers." That's not a worst case. That's the expected timeline, because the fix has to ship in an actual browser update, and browser updates roll out gradually across a user base, not all at once.
Preload is, in Scott Helme's framing, "a one way ticket." OWASP calls the consequences of getting it wrong "PERMANENT." Neither of those is marketing language. That's how the people closest to this mechanism actually describe it.
Pre-flight checklist: should you submit yet?
Before you add preload to a production header, run through this list honestly:
- Have you inventoried every subdomain under your root domain, including ones marketing, sales, or a contractor set up years ago and forgot about?
- Is every one of those subdomains, including staging and internal tools, serving a valid TLS certificate right now, not "will be soon"?
- Has your production header held at
max-age=31536000withincludeSubDomains(nopreload) for at least a few weeks without a single subdomain breaking? - Do you have a documented owner for any third-party-hosted subdomain (a help desk on a vendor's platform, for instance) who can confirm HTTPS support in writing?
- Are you comfortable that if something breaks after submission, you're looking at a 6 to 12 week fix, not a same-day rollback?
If any answer is "not sure," that's your answer for now. Hold at stage 3 of the rollout table above. There's no penalty for staying there for months. The penalty only shows up if you jump to preload before you've actually checked.
Frequently Asked Questions
What is the HSTS preload list?
It's a static list of domains compiled directly into the Chromium browser binary and consumed by Chrome, Firefox, Safari, Edge and other major browsers. Domains on it get HTTPS-only enforcement starting from the very first connection a user's browser ever makes to them, before the browser has had a chance to see that site's own HSTS header even once.
What max-age does preload require?
The floor is 31536000 seconds, one year, sent alongside includeSubDomains and preload in the same Strict-Transport-Security header. That's a minimum, not a target: hstspreload.org's own published example header uses 63072000 seconds (two years), and OWASP recommends two years as standard production practice rather than the bare minimum.
Can I remove a domain from the preload list?
Yes, through the removal form at hstspreload.org, but only once your domain still serves valid HTTPS and sends a Strict-Transport-Security header with the preload token stripped out (not the whole header removed, just that directive). Once submitted, the change reaches most Chrome users in 6 to 12 weeks and can take longer for other browsers, because it ships as part of a browser release rather than propagating live like a DNS change would.
Should I enable preload on a site with old subdomains?
Not until you've inventoried every subdomain, including internal tools, staging environments, and anything hosted by a third party, and confirmed each one serves valid HTTPS. includeSubDomains is a mandatory, all-or-nothing part of preload eligibility with no exceptions list, so a single forgotten HTTP-only subdomain becomes completely unreachable for users until you complete the multi-week removal process described above.