HSTS Explained: Strict Transport Security & Preload
How HSTS stops SSL stripping: the Strict-Transport-Security header, max-age, includeSubDomains, the preload list, and safe deployment.
You configured your site to redirect all HTTP traffic to HTTPS. The padlock shows. It feels done. There is still a crack, and it is at the very beginning of the connection. When a user types your address without https://, or follows an old link, the browser's first request goes out over plain HTTP. That single unencrypted request, before the redirect fires, is where an attacker on the network can wedge themselves in and keep the whole session on HTTP.
HTTP Strict Transport Security, HSTS, seals that crack. It is a small response header that tells the browser a firm rule: for this site, use HTTPS only, never HTTP, and do not let me override it. Once the browser has learned that rule, it upgrades every request to HTTPS on its own before anything leaves the machine, so there is no plain HTTP request left to intercept.
The attack HSTS stops: SSL stripping
To see why the redirect alone is not enough, follow the plain HTTP request. The user types yourbank.example. The browser sends an HTTP request. Your server answers with a redirect to the HTTPS version. Normally the browser follows it and everything is fine.
Now put an attacker on the network path, on shared Wi-Fi or a compromised router. They intercept that first HTTP request. Instead of letting your redirect reach the browser, they fetch the HTTPS site themselves and relay it to the victim over plain HTTP, quietly stripping the encryption. To the victim, the site looks normal. To the attacker, every keystroke, including the password, is readable. This is SSL stripping, and it works precisely because the first request was HTTP and the browser had no prior instruction to insist on HTTPS.
HSTS defeats it by removing the plain HTTP request. If the browser already knows the site is HTTPS-only, it never sends that first HTTP request at all. It rewrites the request to HTTPS internally. The attacker has nothing to strip.
The header and its directives
HSTS is delivered as a response header over HTTPS:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Three directives do the work.
| Directive | Meaning | Notes |
|---|---|---|
| max-age | Seconds the browser enforces HTTPS-only | Required; each visit refreshes the countdown |
| includeSubDomains | Apply the rule to every subdomain too | Only safe when all subdomains serve HTTPS |
| preload | Signal intent to be added to the browser preload list | Read by the list operator, not by the header logic itself |
max-age is the memory. A value of 63072000 is two years. Every time the browser sees the header again it resets the clock, so an active site keeps the protection fresh indefinitely. Setting max-age=0 clears the rule, which is how you turn HSTS off deliberately.
includeSubDomains widens the rule from the exact host to everything beneath it. This is powerful and worth having, but it is also a commitment: every subdomain, including ones you may have forgotten, must serve HTTPS or it will become unreachable.
The bootstrap gap, and how preload closes it
HSTS has one built-in limitation. The browser only obeys the rule after it has seen the header at least once, over HTTPS. On a user's very first visit to your site, before any HSTS header has been received, the browser does not yet know the site is HTTPS-only. That first visit is still exposed to stripping.
The preload list closes this gap. It is a list of domains, maintained by the browser vendors and shipped inside the browser itself, that are hard-coded as HTTPS-only. If your domain is on the list, the browser enforces HTTPS from the very first request, before it has ever contacted your site. There is no unprotected first visit, because the rule arrived with the browser.
Getting on the list requires serving the header with a long max-age, includeSubDomains, and the preload flag, then submitting the domain to the list. That flag is a declaration that you understand and accept the consequences.
Being on the preload list means the HTTPS-only rule lives inside browsers that shipped months ago. If you later cannot serve HTTPS on your domain or a subdomain, you cannot simply remove the header to fix it, because the old browsers never ask your server. Removal from the list, and its propagation into shipped browsers, takes a long time. Only preload once you are certain every name under your domain will serve HTTPS for the foreseeable future.
What HSTS does and does not do
HSTS is narrow and it is good at its job. It also is not a cure-all, and it helps to be clear about the edges.
What it protects against:
- SSL stripping and downgrade attacks, by refusing plain HTTP entirely.
- Accidental HTTP requests, from typed addresses, bookmarks, or old links, which it upgrades before they leave the browser.
- Users clicking through certificate warnings. With HSTS in force, the browser makes the TLS error non-overridable, so a user cannot bypass a warning that might be an active attack.
What it does not do:
- It does not fix a broken or misconfigured TLS setup. It assumes your HTTPS is correct; it just insists it be used.
- It does not protect the first visit unless you are preloaded.
- It does not encrypt anything itself. It is an instruction about which protocol to use, and the encryption is still TLS's job.
Do not launch straight into a two-year max-age with includeSubDomains. If any subdomain is not actually serving HTTPS, you will make it unreachable for everyone who has visited, for the full duration you set, and you cannot easily take it back. Start with a short max-age like 300 seconds, confirm every host under the domain serves HTTPS cleanly, then raise it in steps to the long value before you ever consider preloading.
Deploying HSTS safely
- Get HTTPS fully working first, on the main domain and every subdomain, with valid certificates and a clean redirect from HTTP to HTTPS.
- Start with a short max-age. Something like
max-age=300lets you verify nothing breaks without committing to a long enforcement window. - Add includeSubDomains only after auditing subdomains. Confirm every name beneath your domain serves HTTPS, including internal tools and forgotten hosts, before you extend the rule to them.
- Raise max-age in stages to the long-term value, typically one to two years, once you are confident.
- Preload only when you are sure. Add the
preloadflag and submit to the list after everything above holds, understanding that reversing it is slow. - Keep serving the header on every HTTPS response, so the browser's countdown keeps refreshing and the protection never lapses for returning visitors.
A worked example: the first coffee-shop visit
Follow one user through a hostile network to see where each protection engages. A person opens a laptop on public Wi-Fi and types bank.example into the address bar. They do not type a scheme, so the browser defaults to http://bank.example.
Without any HSTS memory, the browser sends a plain HTTP request. An attacker running a rogue access point sees it, answers as if they were the server, and begins a stripped session. The victim never sees a warning, because a plain HTTP page has no certificate to warn about. Every field they fill in travels in cleartext through the attacker.
Now replay the same visit with a returning user whose browser has already seen the HSTS header on a previous HTTPS visit. The browser holds a rule for bank.example that says HTTPS only. Before any packet leaves the machine, the browser rewrites the address to https://bank.example. The attacker on the network sees only a TLS handshake they cannot forge without a valid certificate. If they present a self-signed or mismatched certificate, the browser shows a hard error that the user cannot click through, because HSTS makes TLS failures non-overridable. The session never starts, which is the correct outcome under attack.
Finally, replay it for a first-ever visitor to a preloaded domain. The browser has never contacted the site, yet the preload list shipped inside the browser already carries the HTTPS-only rule. The upgrade to HTTPS happens on the very first request. There is no unprotected bootstrap request for the attacker to catch. The three cases together show the progression: the redirect alone protects nobody at the first hop, the header protects returning visitors, and preload protects everyone including the first-timer.
Detection signals: is HSTS actually working
Configuring the header and confirming it takes effect are two different tasks. These are the concrete checks that tell you the protection is live.
- The response header is present on HTTPS responses. Fetch the site over HTTPS and read the
Strict-Transport-Securityheader. It must appear on the main document response. A header served only on some paths gives partial coverage. - The header is absent on plain HTTP responses. HSTS is only honoured when received over HTTPS. If your server also emits it over HTTP, browsers ignore that copy, and relying on it is a mistake. The HTTP response should redirect to HTTPS, and the header belongs on the HTTPS side.
- The browser upgrades HTTP requests internally. In browser developer tools, a request typed as
http://should show as an internal redirect with a status such as307 Internal Redirectand no plain HTTP request on the wire. That internal redirect is the visible fingerprint of an active HSTS rule. - The site appears in the browser's internal HSTS store. Chromium-based browsers expose a query page at
chrome://net-internals/#hstswhere you can look up a host and see whether a dynamic or static (preloaded) entry exists, along with its expiry and whether subdomains are included. - Preload status is queryable. The public preload submission site reports whether a domain is pending, included, or has an issue that blocks inclusion. A domain you believe is preloaded but that the list does not show is not actually protected on first visit.
- max-age is large enough to matter. A tiny
max-ageleft over from a test rollout means the rule lapses quickly for users who do not return often. Confirm the production value is the long one you intended, typically one to two years in seconds.
It is easy to confirm HTTPS works and stop there. The value of HSTS is in what it refuses, so test the refusal. Point a browser at the plain HTTP version and confirm it never sends an HTTP request once the rule is cached. Present a deliberately invalid certificate in a test environment and confirm the browser blocks it with no click-through option. If either of those still lets the connection proceed, the rule is not in force the way you think.
HSTS among the related protections
HSTS is one of several controls that keep a browser on the secure path. They solve overlapping but distinct problems, and it helps to see where each one draws its line.
| Mechanism | What it forces | Scope | Covers the first visit |
|---|---|---|---|
| HTTP to HTTPS redirect | Server-side hint to switch schemes | Per response, after the HTTP request is already sent | No |
| HSTS header | Browser-enforced HTTPS-only rule | The host, plus subdomains if set, after one HTTPS visit | No |
| HSTS preload | Same rule shipped inside the browser | The host and subdomains, before any contact | Yes |
| Upgrade-Insecure-Requests (CSP) | Browser upgrades mixed-content subresources to HTTPS | Resources within a page | Not applicable to the top-level navigation |
| HTTPS-only mode (browser setting) | User or policy forces HTTPS attempts everywhere | All sites the user visits | Depends on the browser's fallback behaviour |
The redirect is a courtesy the server offers after the risky request has already left the browser. HSTS moves the decision into the browser so the risky request is never sent. Preload moves that same decision earlier still, into the browser's shipped data, so it applies before the site is ever contacted. The Content Security Policy upgrade-insecure-requests directive is a different tool: it upgrades subresource requests inside a page you are already viewing, and it does not replace HSTS for the top-level navigation.
Common misconceptions
HSTS encrypts traffic. It does not. HSTS is an instruction about which protocol to use. TLS still does the encryption. A site with HSTS but a weak or misconfigured TLS stack is still weak; HSTS only guarantees the browser insists on the HTTPS path, not that the path is well built.
A redirect from HTTP to HTTPS is enough. The redirect is exactly the message an attacker intercepts. The first HTTP request has already been sent by the time your redirect could arrive, and a network attacker answers it before your server does. The redirect is necessary for usability, and it is not a substitute for HSTS.
Setting the header once is enough. The browser counts down max-age from the last time it saw the header. Serve it on every HTTPS response so the countdown keeps refreshing. If you stop sending it, returning visitors slowly age out of protection.
includeSubDomains is free. It is a commitment that every current and future name under the domain serves HTTPS. A forgotten internal host on a subdomain becomes unreachable for anyone whose browser cached the rule, for the full max-age, with no quick way back. Audit before you set it.
Preload can be switched off like the header. Preloaded browsers do not ask your server, so removing the header changes nothing for them. Removal from the list and its propagation into already-shipped browsers takes a long time. Preload is close to a one-way door on the timescale that matters during an incident.
HSTS defends against phishing. It does not. A user tricked into visiting bank-example.test gets HTTPS to the attacker's own domain. HSTS binds a rule to a specific host you control, and it says nothing about lookalike domains.
How HSTS evolved
The problem HSTS addresses became widely understood when SSL stripping was demonstrated as a practical attack on unencrypted first hops. Sites were adding HTTPS, but the default of plain HTTP for typed addresses left a reliable interception window. HSTS was standardised in RFC 6797 in 2012 to let a site declare, in a way the browser would enforce, that it should only ever be reached over HTTPS.
The header alone still left the bootstrap gap, the unprotected first visit before any header had been seen. Browser vendors answered that with the preload list, a hard-coded set of HTTPS-only domains shipped in the browser binary. Over time the list grew from a handful of high-value domains to a large registry, and whole top-level domains have been preloaded so that every name under them is HTTPS-only by default. The direction of travel is clear: the industry keeps moving the HTTPS decision earlier, from a server redirect, to a cached browser rule, to data shipped with the browser, and increasingly toward HTTPS-first behaviour as the browser default regardless of any individual site's configuration.
Serving the header correctly across your stack
The rule sounds simple, and the operational details are where deployments go wrong. A few points decide whether the header actually reaches browsers consistently.
Emit the header at one clear layer. If both your application and a reverse proxy set it, you can end up with duplicate or conflicting values, and the directives that reach the browser become unpredictable. Choose the edge (a load balancer, CDN, or web server) or the application, and set it in exactly one place.
Make sure the header rides every HTTPS response for the document, including deep links a visitor might land on first. A visitor who arrives at an inner page should still receive the rule. Setting it globally at the edge is usually the cleanest way to guarantee that.
Watch how a CDN caches. A cached response that omits the header, served to many users, weakens coverage. Configure the header as an edge behaviour applied to responses rather than something baked into a single cached object, so it is present regardless of cache state.
Confirm subdomains before includeSubDomains. Enumerate every name under the domain, including staging hosts, mail interfaces, status pages, and legacy tools. Each must serve valid HTTPS. One that does not becomes unreachable for the life of the cached rule once you extend the directive to cover it.
Treat HSTS as a staged rollout with checkpoints. Ship a short max-age first and verify the header appears on every HTTPS path. Raise it once you have watched real traffic for problems. Add includeSubDomains only after a full subdomain audit passes. Add preload and submit the domain last, when you are confident the HTTPS-everywhere state is permanent. Each step is cheap to reverse until the one before it, and preload is the step you cannot easily take back.
Frequently asked questions
Does HSTS require anything from the user? No. It is entirely server-declared and browser-enforced. The user does nothing and sees nothing, except that attempts to reach the site over HTTP silently become HTTPS and certificate errors can no longer be clicked past.
What max-age should I use in production?
Once you have confirmed every host serves HTTPS cleanly, a value of one to two years in seconds is standard. The preload list also expects a long max-age. Start much lower during rollout so a mistake expires quickly, then raise it in stages.
Can HSTS lock me out of my own site?
Effectively yes, if you set includeSubDomains and a subdomain then fails to serve valid HTTPS. Browsers that cached the rule will refuse to connect for the remainder of max-age, with no override. This is why you audit subdomains and ramp max-age slowly before committing.
How do I turn HSTS off?
Serve Strict-Transport-Security: max-age=0 over HTTPS. Browsers that receive it clear the rule. This only reaches browsers that visit again over HTTPS, and it does nothing for preloaded entries, which must be removed through the list's own removal process.
Is HSTS still needed if I am preloaded? Yes. Keep serving the header. Preload covers the first visit, and the header keeps the dynamic rule fresh for browsers and for any path where the static entry does not apply. The two work together.
Does HSTS help against a compromised or malicious certificate authority? Not on its own. HSTS forces HTTPS but still trusts any certificate that chains to a trusted authority. Controls like Certificate Transparency and, historically, certificate pinning address the authority-trust problem, and they sit alongside HSTS rather than inside it.
Should I set the preload flag before I am ready to preload?
No. The preload flag is a declaration that you accept the consequences of being on the list, including how hard it is to reverse. Only add it, and only submit the domain, once every name under it serves HTTPS and you are confident that will hold.
HSTS is one header that removes a whole class of attack, by taking the decision of "HTTP or HTTPS" out of the network's hands and putting it inside the browser. Combined with the preload list, it even protects the first-ever visit. Deploy it in careful steps, make sure every subdomain is ready before you commit, and you close the downgrade gap that a padlock alone leaves open.
For how downgrade and interception attacks show up in real intrusions, and the network positions that make them possible, our exploit intelligence dashboard tracks the techniques attackers use to sit between users and the sites they trust.
Related guides
Sources & further reading
Related guides
- SSL Stripping: Downgrading HTTPS to HTTP & the HSTS Defense
How SSL stripping downgrades a victim's HTTPS connection to plain HTTP from an on-path position, and why HSTS and preload close the gap.
- Active Directory Security: Structure, Tiering & Hardening
How Active Directory is built, the tiered administration model, the weaknesses attackers exploit, and a hardening checklist for defenders.
- ARP Spoofing Explained: LAN Poisoning & DAI Defense
How ARP cache poisoning lets an attacker intercept traffic on a local network, why the protocol is trivial to abuse, and how to shut it down.