</>CodeWithKarani

The HSTS Preload List Is Almost One-Way: Check This Before You Submit

Karani GeoffreyKarani Geoffrey8 min read

Preloading HSTS feels like free security. You add one token to a header, submit your domain to a list, and browsers will refuse to talk to your site over plain HTTP forever, before the first request, with no downgrade possible. Every hardening checklist tells you to do it. Almost none of them tell you that it is one of the few configuration decisions on the web that you effectively cannot take back.

Six months later a colleague needs a subdomain to accept a plain-HTTP webhook from a legacy vendor that cannot do TLS, or an internal box on a subdomain that never got a certificate. It simply does not work, and there is no server-side setting that fixes it, because the rule is not on your server. It is compiled into the browser. You submitted it, and getting out takes months.

I am not telling you never to preload. Preloading is genuinely strong protection against the first-request downgrade attack. I am telling you it is a near-irreversible, whole-tree, forever commitment, and you should make it with your eyes open, not because a checklist had a checkbox.

HSTS preloading hardcodes your domain into the browser's source, so there is no server-side undo. Once preloaded, every subdomain, including ones you have not built yet, must serve valid HTTPS forever, and an expired certificate makes the whole site unreachable with no click-through warning. Removal means serving Strict-Transport-Security: max-age=0 and dropping the preload token, then submitting the removal form and waiting weeks for a Chromium release plus months for users to update. Before you submit: confirm every current and future subdomain can do HTTPS, and that your certificate renewal cannot silently fail.

What "preload" actually changes

Ordinary HSTS is a response header:

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

Without the preload list, this header only protects a browser after its first successful HTTPS visit. The browser sees the header, remembers "always use HTTPS for this host for the next two years", and enforces it. The gap is the very first visit: before the browser has ever seen the header, an attacker on the network can downgrade that request to HTTP. That first-visit gap is the whole reason the preload list exists.

The preload list closes it by shipping your domain inside the browser. Chromium maintains a hardcoded list of domains that must always use HTTPS, and Firefox, Safari and Edge consume versions of it. When your domain is on that list, a fresh browser that has never visited you still refuses plain HTTP from the very first request. There is no gap. There is also, crucially, no negotiation: the browser does not ask your server anything. The rule lives in the browser binary that shipped to the user.

Why this is nearly one-way

Every other TLS decision you make is reversible from your server. Wrong cipher suite, bad redirect, misissued certificate: you change a config, reload, done. Preloading is the exception, and it is the exception precisely because the enforcement is not on your server. Three consequences follow, and each one has bitten people.

1. Every subdomain must do HTTPS, including ones that do not exist yet

The includeSubDomains directive is mandatory for preload eligibility. Once you are on the list, anything.yourdomain.com must serve valid HTTPS. Not just today's subdomains, all future ones. The intern who spins up test.yourdomain.com on plain HTTP for an afternoon will find it unreachable in every modern browser and will have no idea why, because nothing on that box is misconfigured. The rule is in the visitor's browser.

2. An expired certificate takes the whole site down, with no way past

Normally an expired certificate shows the user a warning they can click through: "your connection is not private, proceed anyway". On a preloaded domain there is no proceed-anyway. HSTS turns the certificate error into a hard failure. The site is simply gone until you fix the certificate. A renewal that silently failed over the weekend is now a total outage on Monday, not a scary warning. If your certificate automation can fail quietly, preloading raises the blast radius of that failure to "entire domain, no bypass".

3. Removal is a multi-month wait, not a config change

To come off the list you serve Strict-Transport-Security: max-age=0 and remove the preload token from your header, which makes you eligible for the removal form at the official submission site. Then you wait. Removal has to ship in a new Chromium release, which takes weeks, and then it only reaches a user when their browser updates, which for the long tail is months. Throughout that window your domain is still enforced for everyone who has not updated. There is no fast path and no emergency override.

Getting on the list vs getting off it Submit add preload token, submit form. Minutes of work. Remove serve max-age=0 drop preload token submit removal form eligibility checked wait Chromium release weeks wait user updates months
Submitting is minutes of work. Reversing it is a process measured in browser release cycles, and you do not control any of them.

The checklist to run before you submit

Preloading is right for plenty of sites. It is right when the whole domain is HTTPS, will stay HTTPS, and your certificate renewal is reliable. Confirm all of this before you add the token, because after is too late.

  1. Every subdomain currently serves valid HTTPS. Enumerate them all: www, api, mail, vpn, staging boxes, that one legacy thing. Any that cannot do HTTPS is a blocker. This includes internal-only names if they resolve publicly.
  2. No current or foreseeable need for plain-HTTP on any subdomain. Legacy webhooks, IoT callbacks, old payment terminals, vendor integrations that cannot do TLS. If any of these live on a subdomain, preloading breaks them permanently.
  3. Certificate renewal cannot fail silently. This is the big one. On a preloaded domain, a missed renewal is a full outage with no bypass. You need renewal that is automated and alerted, so a failure pages you before expiry, not after.
  4. You have run the header with a long max-age, live, for weeks, without incident, before adding preload. Treat the non-preloaded long max-age as the dress rehearsal. If anything breaks there, it would have been unfixable once preloaded.
  5. The apex is the domain you are committing. Preloading is per registrable domain and it takes the whole subtree with it. You are not signing up one host; you are signing up the tree.

Verification: prove you are (or are not) on the list

Do not guess your status. You can inspect the header your server sends and check the browser's own preload state.

curl -sI https://yourdomain.com | grep -i strict-transport-security
# expect: strict-transport-security: max-age=63072000; includeSubDomains; preload

The header carrying the preload token only means you are eligible; it does not mean you are on the list. To check actual browser enforcement, open the browser's HSTS query page (in Chromium, chrome://net-internals/#hsts) and query your domain. If it shows a static entry, you are on the preload list and enforcement is baked in. A dynamic entry only means this browser has visited you and remembered the header, which is ordinary HSTS and is reversible by comparison.

To confirm subdomain coverage will not bite you, resolve and test each subdomain over HTTPS before committing:

for h in www api mail staging; do
  echo -n "$h: "; curl -sSo /dev/null -w "%{http_code}\n" "https://$h.yourdomain.com" || echo "FAILS HTTPS"
done

Any host that fails HTTPS here is a host that will be unreachable after you preload.

What people get wrong

"Preload is just a stronger version of the header, so more is better." No. Ordinary HSTS is reversible from your server; preload is reversible only through someone else's release process. They are different commitments with the same syntax, and the extra token changes the risk category entirely.

"We can just remove the header if something breaks." Removing the header does nothing for browsers that already have you on the static list; they never asked your server. You must serve max-age=0, submit the removal form, and wait out the release cycle. Deleting the header only stops new eligibility.

"includeSubDomains is optional." It is required for preload eligibility, and it is the directive that turns "my main site" into "every host under my domain forever". People add it without registering that it commits subdomains that do not exist yet.

"An expired cert just shows a warning." Not on a preloaded domain. It is a hard, unbypassable failure. This is why silent renewal failure and preloading are a dangerous pair, and why I would not preload a domain whose renewal I could not prove was alerted. If you are not sure yours is, read how Certbot renewal fails silently and how to alert on it first.

When it is still broken

  • A subdomain is already unreachable and you are already preloaded. There is no quick fix. Give the subdomain a valid certificate and serve it over HTTPS; that is the only path, because you cannot make the browser accept plain HTTP for it. If TLS on that box is genuinely impossible, move the service to a different registrable domain that is not preloaded.
  • You need to remove the domain entirely. Serve Strict-Transport-Security: max-age=0 without the preload token on the apex, confirm it, then submit the official removal form, and communicate to stakeholders that enforcement continues for months while releases and updates roll out. Plan around the wait; do not promise it will be fast.
  • A certificate expired on a preloaded domain right now. This is a live outage. Renew or reissue the certificate immediately; there is no header change that helps because the header is not what is blocking. Then fix the renewal alerting so it cannot recur, since next time there is no warning page to soften it.
  • You are still deciding whether to preload at all. Run the long-max-age header without preload for a good while. It gives you almost all the protection for real visitors, stays fully reversible, and surfaces any subdomain or renewal problem while you can still fix it cheaply. Pair it with a solid TLS and reverse-proxy config and you have the security without the trap.

Preloading is a good tool held the wrong way by most checklists. The rule to remember is simple: everything else in your TLS setup you can undo from a terminal in minutes; this one you undo through a browser vendor's release calendar over months. Commit accordingly.

Frequently asked questions

Can I remove my domain from the HSTS preload list?
Yes, but slowly and not from your server. You serve Strict-Transport-Security: max-age=0 and drop the preload token from your header, which makes you eligible for the removal form at hstspreload.org/removal. After that, removal has to ship in a new Chromium release, which takes weeks, and then only reaches each user when their browser updates, which for the long tail takes months. There is no fast path or emergency override.
What happens if my certificate expires on a preloaded HSTS domain?
The whole site becomes unreachable with no click-through warning. Normally an expired certificate shows a proceed-anyway page, but HSTS turns that into a hard failure, and on a preloaded domain there is no bypass at all. A renewal that silently failed becomes a total outage rather than a warning, which is why you should only preload a domain whose certificate renewal is automated and alerted.
Does HSTS preload apply to subdomains?
Yes, and it is mandatory. The includeSubDomains directive is required for preload eligibility, so once you are on the list every subdomain must serve valid HTTPS forever, including subdomains that do not exist yet. A subdomain spun up on plain HTTP later will be unreachable in modern browsers even though nothing on that server is misconfigured, because the rule lives in the visitor's browser.
Is HSTS preload different from the normal Strict-Transport-Security header?
Fundamentally, yes. The ordinary header is enforced by the browser only after its first HTTPS visit and is fully reversible from your server. Preloading ships your domain inside the browser binary so it is enforced from the very first request and can only be undone through a browser vendor's release cycle over months. They share syntax but are different risk categories, so do not add the preload token casually.
#HSTS#TLS#HTTPS#Certificates#Web Security#Hardening
Keep reading

Related articles