Here is the scenario the HSTS preload tutorials never walk you through. You added preload to your Strict-Transport-Security header a year ago, submitted your domain at hstspreload.org, and it shipped inside Chrome. Good for you — that was the responsible thing to do. Now you’ve acquired a subdomain you need to serve over plain HTTP for a few weeks: an embedded device that can’t do TLS, a legacy internal host, a partner’s box you don’t control. You remove the preload directive from your header. You submit the removal request. And then you wait. Not hours. Not days. Months — long enough that “I’ll just turn it off” turns into a line item on next quarter’s roadmap.
That asymmetry — trivial to turn on, brutally slow to turn off — is the whole story of HSTS preloading, and it’s the part that gets waved away when someone tells you preloading is just “HSTS but stronger.”
What the header does, and the hole it can’t cover
HSTS itself is simple and good. Strict-Transport-Security: max-age=31536000; includeSubDomains is a header your server sends over HTTPS that tells the browser: for the next year, never talk to me over plain HTTP, and don’t let the user click through a certificate warning. RFC 6797 standardized it back in 2012. Once a browser has seen that header, a network attacker can’t downgrade the connection or strip the TLS, because the browser refuses to even try HTTP for your domain.
The word doing the work in that paragraph is seen. HSTS is trust-on-first-use. The protection only exists after the browser has received the header at least once over a good connection. The very first request a browser ever makes to your domain — before it has the header — can still go out over HTTP, and that first request is exactly where an attacker sitting in the path can intercept it, redirect it, and keep the victim on HTTP forever. RFC 6797 names this honestly in its security considerations: the bootstrap MITM vulnerability. The header can’t protect the connection that’s supposed to deliver the header.
Preloading is the fix for that one gap. If the browser ships already knowing your domain is HTTPS-only, there’s no insecure first request to intercept. The protection is in place before the user has ever visited you.
The catch: it’s not your list
Here’s the thing nobody says plainly. The preload list is not a record you publish and control, like a DNS entry or an HTTP header. It is a file inside the browser. In Chromium it’s literally a source file — net/http/transport_security_state_static.json — compiled into the binary and shipped with every release. Firefox, Safari, and Edge import essentially the same Chromium-maintained list. RFC 6797 anticipated this (§12.3 describes a “pre-loaded list” of HSTS hosts, configured by the browser vendor “in a manner similar to how root CA certificates are embedded”), but it never standardized the preload directive or the submission process. That token in your header and the site at hstspreload.org are a de facto mechanism run by Google, not a protocol feature. There is no RFC that says how you get on, or off.
So when you preload, you are not configuring your own infrastructure. You are submitting a patch to a data file in someone else’s software and asking them to ship it to a few billion people. And the requirements to get accepted are strict on purpose: a valid certificate, a max-age of at least one year (31536000 seconds), includeSubDomains, the preload token, and an HTTP-to-HTTPS redirect on the same host. Those are reasonable gates. They are also a contract you’re signing on behalf of your entire domain tree, indefinitely, with a counterparty whose release schedule you have no say in.
Removal is the part that should scare you
Adding an entry is fast because the bar is just “prove you’re already HTTPS-only.” Removal is slow because of how the list ships. To get off, you first have to make your site eligible to leave: serve a valid HSTS header that no longer contains preload (you can’t just rip HSTS out — a domain serving no header at all isn’t removable). Then you submit a removal request. Then the entry comes out of the list in a future build. Then that build has to roll out. Then users have to actually update their browsers.
That last step is the killer. The list reaches users through browser updates, and there is no mechanism — none — to reach a browser that hasn’t updated. hstspreload.org’s own removal page tells you to expect months before the change reaches most users, and “most” is not “all.” Someone running an old browser build keeps enforcing the old list. A fresh install from a stale installer pulls a list from whenever that installer was cut. For all practical purposes, preloading a domain is permanent, and you are betting that you will never, on any timeline shorter than a year, need to serve anything on that name or any name under it over plain HTTP.
And includeSubDomains makes that bet bigger than people realize, because preload requires it. You are not promising HTTPS for example.com. You are promising it for every host that will ever exist under example.com, forever, including the ones nobody has created yet. The day someone in another department stands up legacy-vendor-portal.example.com on a box that can’t terminate TLS, it is simply unreachable in compliant browsers, and the fix is the months-long removal dance for the entire parent domain.
The trade you’re actually making
I want to be careful here, because preloading is not a mistake. For a domain that is HTTPS-only and intends to stay that way — your bank, your primary product, anything where the bootstrap MITM is a real threat — it closes a genuine hole and you should do it. The point isn’t “don’t preload.” The point is to see the trade clearly instead of treating it as a free upgrade.
What you gain is closing the first-visit gap. What you give up is the ability to change your mind on your own schedule. You’ve moved a decision about your domain’s HTTPS enforcement out of your DNS and your servers — where you can change it in seconds — and into a binary maintained by browser vendors, where you change it in fiscal quarters. The bootstrap vulnerability didn’t disappear, either; it moved. It used to be trust-on-first-use, exploitable on a user’s first-ever visit. Now it’s trust-on-first-update — the gap belongs to the brand-new domain not yet in any shipped list, and to the browser running a months-old build. Smaller window, real window.
That’s the same bargain as the root CA store, and we accept it there for the same reason: centralizing trust into the browser is the only thing that actually scales to the whole web. It works. But “it works” and “it’s free” are different claims, and the preload list is sold as the second when it’s only the first. Before you add that directive, ask the question the tutorials skip: am I prepared to be HTTPS-only on this name and everything under it for longer than I can plan for? If yes, preload with confidence. If you’re not sure, the honest move is to run a long max-age without the preload token first — you get almost all the protection for returning visitors, and you keep the keys to your own door.