skip to content

HSTS and Preload

The Strict-Transport-Security header and the preload list behind it: max-age, includeSubDomains, and why the first plaintext request is the risk. Interviewers ask if HTTPS is enforced or just offered.

part ofWeb protocols & securityoverview, primer and where to startread it →
on this pageshow

questions

6

What does a Strict-Transport-Security response header tell a browser to do, and why can it not protect the very first request?

level: juniorimportance: must knowfreq 74%

answer

  1. enforcement, not an offer
  2. browser stores per-host state
  3. rewrite happens before any request
  4. policy arrives only in a response
  5. first plaintext visit is the gap

basics

~20 s

Strict-Transport-Security tells a browser to rewrite every later request for that host to https before the request leaves the machine, for max-age seconds. The browser learns the policy only from a response, so the first plaintext request stays unprotected.

solid answer

~50 s

`Strict-Transport-Security` is a response header field a host sends over a secure connection. Once a browser processes it, that name becomes a **Known HSTS Host** for `max-age` seconds, and from then on the browser rewrites an `http` URL for that host to `https` at URI-loading time, before a request is even built, so nothing plaintext goes on the wire. That is what separates it from a plaintext listener that redirects: a redirect needs the plaintext request to be sent and answered first. The gap is the bootstrap. A browser that holds no entry for the name has nothing to apply, so the first request from a fresh device or profile still leaves as plaintext, and that request is exactly where an attacker sits. RFC 6797 calls this the Bootstrap MITM Vulnerability; only a list of names shipped inside the browser closes it.

code

http · 3 lines
http
HTTP/1.1 200 OK
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Type: text/html; charset=utf-8

go deeper

for a junior

Recall the shape: a response header sent over TLS, a lifetime in seconds, and a browser that from then on turns http into https by itself. Then be able to say why a brand-new device is still exposed once.

for a middle

Explain where the rewrite happens — before a request is built, not after one is answered — and why that is a different guarantee from a plaintext listener returning a redirect.

for a senior

Show that you know the limit as well as the benefit: the bootstrap window reopens on every fresh device, profile or expired entry, and only a list shipped inside the browser closes it.

for a principal

Frame it as a claim about a whole name rather than a page, and be ready to say what evidence would convince you the claim holds across every host the organisation publishes.

## The header, and the state it creates `Strict-Transport-Security` is a response header field defined by RFC 6797. A host sends it on a response carried over a secure connection, and a browser that processes it records an entry for that host name. The specification calls such a host a **Known HSTS Host**; a name with no entry is an *unknown HSTS Host*. Two things are taken from the field and stored with the entry: - `max-age`, a **REQUIRED** directive whose value is a delta-seconds count — how long the entry lives, measured from the moment the field was received, not from when the response was generated or the certificate was issued; - `includeSubDomains`, an **OPTIONAL**, valueless directive that widens the entry from the one name to every name beneath it. Nothing about that entry travels on the wire afterwards. It is per-host state inside the browser, and it is consulted *before* a request exists. ## What the browser does with the entry At URI-loading time — the moment the browser is about to turn a URL into a request, whether the user typed a bare host name, followed a link, or a document referenced a resource — it checks the host against its stored entries. On a match it rewrites the URL: - an `http` scheme becomes `https`; - an explicit port `80` becomes `443`; - any other explicit port is kept exactly as written; - where the URL carried no port at all, none is added. The request that is finally built is the rewritten one. A plaintext request for that host is never constructed, so none is ever sent. ## Enforcement versus offer This is the distinction an interviewer is looking for behind the sentence "we use HTTPS". | | plaintext listener that redirects | stored HSTS policy | |---|---|---| | Where the decision is taken | on the host, after the request arrives | in the browser, before the request is built | | What crosses the network unprotected | the request line, the headers, and whatever the browser attached to them | nothing, because no plaintext request is made | | Who can interfere | anyone on the path to the plaintext listener | nobody, because there is no plaintext leg to sit on | | A browser holding no entry | the request goes out plaintext | the request goes out plaintext — there is nothing to apply | The last row is the honest limit, and it has a name. ## The bootstrap gap The policy can only be learned from a response, and the response can only be fetched by making a request. The first request to a name a browser has never held an entry for therefore leaves as whatever the URL said — and if the user typed a bare host name, that is plaintext. RFC 6797 names this the **Bootstrap MITM Vulnerability**. An attacker on the path answers that request themselves, keeps the connection on plaintext, and simply never lets the header through; the browser never learns there was a policy at all. The window opens again on every fresh start: a new device, a new profile, cleared browsing state, or an entry whose `max-age` has run out. Each visit within the lifetime slides it forward, because the clock restarts from reception each time a valid field is processed. Only one facility closes the gap: a list of names shipped inside the browser itself — the **HSTS Pre-Loaded List** — so that the name is already known before any request is made. That list is run by browser vendors and is not part of RFC 6797. ## What does not deliver a policy Three rules trip people up, and all three are in the specification: 1. A host **MUST NOT** send the field on a response carried over non-secure transport, and a browser **MUST** ignore one that arrives that way. A policy you cannot authenticate is a policy an attacker can plant or suppress. 2. A policy expressed through an `http-equiv="Strict-Transport-Security"` attribute in markup **MUST NOT** be heeded. It is a response header field or it is nothing. 3. If more than one such field arrives on a single secure response, only the **first** is processed. A second one further down does not refine or override it. ## Where it bites in practice Picture a school district portal reached by a parent typing a bare host name into an address bar, on a phone joined to whatever network the car park offers. The district serves TLS and its plaintext listener redirects, so the parent sees a padlock and the site looks enforced. It is not, for that parent, on that phone, on that trip: one unprotected request went out first, and an attacker sitting on that network never had to defeat anything. Once the header is processed, every later request from that phone starts as `https` — until the entry expires or the phone is replaced. Finally, note what the policy does *not* claim. It governs the scheme the browser uses when making a request to this host. It says nothing about what a returned document then references, and nothing about what the host's certificate proves.

  • A host sends Strict-Transport-Security on a response carried over plain HTTP. What happens?
    Nothing. A host must not emit the field over non-secure transport, and a browser must ignore one that arrives that way. The reason is authentication: on a plaintext connection an attacker can insert a field, alter its `max-age`, or strip it, so a browser that honoured it could be made to store a policy it was never given — or be kept from storing the real one.
  • Does HSTS make an attacker's certificate usable if the user approves it?
    No. On a Known HSTS Host the browser must terminate the connection on any secure transport error, and browsers are advised to offer no way to proceed. A policy whose whole claim is that this name cannot be reached unauthenticated would be worthless if a click restored the attacker's path.
  • Two Strict-Transport-Security fields arrive on one secure response. Which applies?
    Only the first. A browser processes the first such field on the response and ignores the rest, so a second field cannot refine, lengthen or cancel the first. In practice this matters when more than one layer in front of the host adds the header: the one nearest the front wins, and the other is silently discarded.

Telling every visitor at the front door to use the side entrance still requires them to walk up to the front door once. HSTS is the note the visitor writes in their own diary, so on every later trip they set off for the side entrance from home.

saying these in an interview costs you the question

  • Thinks the header encrypts the response it arrives on.
  • Says a redirect to https gives the same guarantee as a stored policy.
  • Believes the policy protects a device that has never reached the host.
  • Thinks a policy in a meta element is honoured by browsers.
  • Assumes a policy sent over plain HTTP still applies.
open as a page

In a Strict-Transport-Security header, what is the max-age directive counting, and what does max-age=0 signal?

level: middleimportance: must knowfreq 62%

basics

~20 s

The Strict-Transport-Security max-age directive is a required delta-seconds value counting from the moment the browser received the header; it is how long that host stays a Known HSTS Host. max-age=0 tells the browser to drop the stored entry.

open as a page

A browser stored Strict-Transport-Security with includeSubDomains for a parent domain — which later host names does it rewrite, and how?

level: middleimportance: should knowfreq 50%

basics

~20 s

With includeSubDomains, the stored entry matches the parent name itself and any name whose rightmost labels are that parent — a superdomain match, compared label by label. Matching URLs get http rewritten to https and an explicit port 80 mapped to 443.

open as a page

On a Known HSTS Host, why must a browser terminate the connection on any secure transport error, with no user override?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Because the party able to produce the error is the party the policy exists to exclude. RFC 6797 requires termination on any secure transport error at any severity, and advises browsers to offer no way to proceed, so the click-through that would restore the attack is gone.

open as a page

Strict-Transport-Security with includeSubDomains and a year-long max-age is deployed — how do you safely roll it back for one subdomain?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Keep serving TLS and shorten max-age on responses until stored entries are nearly expired, then send max-age=0 over a secure connection to clear them, and only then stop sending the field. Browsers that never return keep the old entry to its full term.

open as a page

Should a district submit its parent domain to the HSTS Pre-Loaded List with includeSubDomains, and what does that commit?

level: principalimportance: nice to knowfreq 33%

basics

~20 s

Only after every name under the domain is enumerated and confirmed to serve TLS. A pre-loaded entry closes the unprotected first request, but it commits present and future subdomains, is operated by browser vendors, and is slow to leave.

open as a page