Skip to content

Add precision in HSTS page #39853

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jun 19, 2025
Merged

Add precision in HSTS page #39853

merged 18 commits into from
Jun 19, 2025

Conversation

vobarian
Copy link
Contributor

@vobarian vobarian commented Jun 8, 2025


Description

  • Make the documentation reflect the terminology and details of the RFC more accurately
  • Instead of "site" or "website", clarify HSTS policy as applying to domain name without port, and not IP addresses; using terminology of "Known HSTS Hosts" and host's domain name
  • Explain more clearly the request response cycle and how HSTS applies to subsequent requests
  • Illustrate relationship between HTTP-to-HTTPS redirects and HSTS; both should be used in tandem
  • Use example.com instead of foo.com (the latter is some spammy domain squatting site)
  • Make the description section start out with the description instead of the threat model; split threat models into a separate section and expand upon how HSTS addresses them
  • Replace example scenario with a detailed step by step explanation of the host and browser interaction
  • Reorder example scenario to come after explanatory content

Motivation

Previous documentation lacked clarity and used ambiguous terms.

Additional details

https://www.rfc-editor.org/rfc/rfc6797

Related issues and pull requests

Fix #39042

* Make the documentation reflect the terminology and details of the RFC more accurately.
* Instead of "site" or "website", clarify HSTS policy as applying to domain name without port, and not IP addresses; using terminology of "Known HSTS Hosts" and host's domain name
* Explain more clearly the request response cycle and how HSTS applies to subsequent requests
* Illustrate relationship between HTTP-to-HTTPS redirects and HSTS; both should be used in tandem
@vobarian vobarian requested a review from a team as a code owner June 8, 2025 00:21
@vobarian vobarian requested review from hamishwillee and removed request for a team June 8, 2025 00:21
@github-actions github-actions bot added Content:HTTP HTTP docs size/m [PR only] 51-500 LoC changed labels Jun 8, 2025
Your browser remembers `example.com` as a Known HSTS Host, and that it specified `includeSubDomains`.
7. A few weeks later, you are at the airport and decide to use the free Wi-Fi. But unknowingly, you connect to a rogue access point running on an attacker's laptop.
8. You open your browser to `http://login.example.com/`. Because your browser remembers `example.com` as a Known HSTS Host and the `includeSubDomains` directive was used, your browser uses HTTPS.
9. The attacker intercepts the request with a fake HTTPS server, but does not have a valid certificate for the domain.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[markdownlint] reported by reviewdog 🐶
MD029/ol-prefix Ordered list item prefix [Expected: 6; Actual: 7; Style: 1/2/3]

@@ -8,10 +8,11 @@ browser-compat: http.headers.Strict-Transport-Security

{{HTTPSidebar}}

The HTTP **`Strict-Transport-Security`** {{Glossary("response header")}} (often abbreviated as {{Glossary("HSTS")}}) informs browsers that the site should only be accessed using HTTPS, and that any future attempts to access it using HTTP should automatically be upgraded to HTTPS.
The HTTP **`Strict-Transport-Security`** {{Glossary("response header")}} (often abbreviated as {{Glossary("HSTS")}}) informs browsers that the host should only be accessed using HTTPS, and that any future attempts to access it using HTTP should automatically be upgraded to HTTPS.
Additionally, on future connections to the host, the browser will not offer the user a means to bypass secure connection errors, such as an invalid certificate.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean if that you have connected once, and an attacker tries to insert itself somehow and offer an invalid certificate you can't click through "trust this"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. For a Known HSTS Host, conformant browsers do not allow the user to bypass TLS errors. In Chrome for example, without HSTS, you can click "Advanced" and then "Proceed to {domain} (unsafe)". With HSTS, instead Chrome shows this message:

You cannot visit {domain} right now because the website uses HSTS. Network errors and attacks are usually temporary, so this page will probably work later.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we're going to be precise about terms here, like using "host" instead of "site" (and I think we should) then we have to give people a way to understand what these terms mean. Suggestion would be to link to the glossary entry for Host, but unfortunately that isn't very precise. Perhaps we could improve on that entry?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might also say "the host (and optionally its subdomains)" here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was unsure how far to distinguish between "host" and "domain name". As far as I understand, a "host" in general can be an IP address or domain name, but HSTS does not allow IP addresses to be a Known HSTS Host, so for HSTS purposes domain name is what matters. Domain name is clearer to me than host.

However, I was trying to imitate RFC 6797 in distinguishing between host as the active participant (server) and domain name as an identifier for the host. For example, the RFC authors' usage is:

When establishing an HTTP connection to a given host, however
instigated, the UA examines its cache of Known HSTS Hosts to see if
there are any with domain names that are superdomains of the given
host's domain name.

They speak of the "host's domain name" in the possessive has-a sense, and refer to "connecting to" the host, not the domain name.

I tried to honor that usage but it may be overly pedantic; in the end the only thing HSTS cares about is the domain name. I tried to make that clear in the Description section.

Probably the most precise (but overly verbose) way to say it while also illuminating "host" would be:

informs browsers that the host (identified by its domain name, and optionally its subdomains) should only be accessed using HTTPS

It introduces too many thoughts in the sentence though. Alternatively, it is technically a little sloppy but gets right to the point:

informs browsers that the domain name (and optionally its subdomains) should only be accessed using HTTPS

A third option would be to add another sentence explaining it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good question. I suppose the point is that host and domain name are not the same thing: host is a server, domain name is its name (how it is identified).

informs browsers that the host (identified by its domain name, and optionally its subdomains) should only be accessed using HTTPS

I mostly like this, although there's ambiguity in "and optionally its subdomains" - someone could think the subdomains are part of the things that identify the host. It's really more like: "host (identified by its domain name), and optionally its subdomains,". But I agree that this is too much for the first sentence.

informs browsers that the domain name (and optionally its subdomains) should only be accessed using HTTPS

I like this less because it's too inaccurate: a domain name isn't a thing you access.

Overall, I think just leaving this with "host" linked to the glossary is fine, and then describing how a host is identified in the Description section.


> [!NOTE]
> This is more secure than configuring a HTTP to HTTPS ({{HTTPStatus("301")}}) redirect on your server, as the initial HTTP connection is still vulnerable to a man-in-the-middle attack.
> HSTS should be used in addition to configuring an HTTP to HTTPS ({{HTTPStatus("301")}}) redirect on your server, as the initial HTTP connection is still vulnerable to a man-in-the-middle attack.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't quite right. HSTS doesn't prevent the initial connection being vulnerable either unless the browser has the preload list set up.

The original text is more like "using HSTS will always force the upgrade after first connection, and this is more safe that using a server redirect. I am not sure why that is true, other than perhaps the facility to prevent any other kinds of bypasses such as accepting poor certificates, so I think your structure is better.

Suggested change
> HSTS should be used in addition to configuring an HTTP to HTTPS ({{HTTPStatus("301")}}) redirect on your server, as the initial HTTP connection is still vulnerable to a man-in-the-middle attack.
> HSTS should be used in addition to configuring an HTTP to HTTPS ({{HTTPStatus("301")}}) redirect on your server.
> The initial HTTP connection is still vulnerable to a man-in-the-middle attack, but this can be mitigated using a browser HSTS preload list.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I believe the original text was trying to explain why a redirect to HTTPS alone is insufficient (since this has long been a practice even before HSTS was introduced), but the way it was worded could be misread to imply that they are mutually exclusive. I wanted to emphasize that both should be used (per RFC section 7.2), but when I rewrote the first clause I missed that the second clause ("as the...") becomes wrong, because it's introduced as a reason but does not supply a supporting reason to the first clause.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I'm hoping @wbamberg will look at this in the week. I'll try look at it again Friday.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should remove this note. It's a somewhat subtle point, and hard to express in a short note without either being misleading, or trying to pack too much into a short sentence, which makes it very hard to understand. Let's keep the preamble here to an overview of what HSTS itself does, and talk about the context in which it is used in the description, where we have the space to explain it all properly.

I agree with @vobarian that the likely intention of the original:

Note: This is more secure than configuring a HTTP to HTTPS (301) redirect on your server, as the initial HTTP connection is still vulnerable to a man-in-the-middle attack.

is "This is more secure than only configuring a HTTP to HTTPS (301) redirect ", and I think your version is definitely better, but it is (I think) going to be hard for people to understand, and doesn't account for the case of first-ever (or first-after-HSTS-expiry) connection.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I thought the original note box was awkward so high on the page. I'll look into incorporating the redirect recommendation into the description.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I did wonder while writing my review if we ought to have some explicit stuff about using this header in tandem with redirection, and I think it might help some of the stuff discussed below about the "Threat models" section. But that's more extensive surgery than we may want to contemplate :).

Copy link
Collaborator

@hamishwillee hamishwillee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wbamberg I've given this a quick scan and will have a more thorough look tomorrow. As you've just gone around the loop on this topic, would you be interested in looking too?

Copy link
Contributor

github-actions bot commented Jun 9, 2025

Preview URLs

Flaws (3)

URL: /en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security
Title: Strict-Transport-Security header
Flaw count: 3

  • unknown:
    • No generic content config found
    • no blog root
    • no blog root

(comment last updated: 2025-06-19 00:10:47)

Copy link
Collaborator

@wbamberg wbamberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I think this is much better.


When the expiration time specified by the `Strict-Transport-Security` header elapses, the next request to the host via HTTP will proceed with insecure HTTP instead of automatically using HTTPS. Whenever the `Strict-Transport-Security` header is delivered to the browser, it will update the expiration time for that host.
Using a fixed value for `max-age` can prevent the HSTS policy from expiring, as each subsequent response will push the expiration farther into the future.
Should it be necessary to disable Strict Transport Security, setting `max-age=0` (over an HTTPS connection) will immediately expire the HSTS policy, allowing access via HTTP.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Should it be necessary to disable Strict Transport Security, setting `max-age=0` (over an HTTPS connection) will immediately expire the HSTS policy, allowing access via HTTP.
To disable Strict Transport Security, setting `max-age=0` (over an HTTPS connection) will immediately expire the HSTS policy, allowing access via HTTP.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels a little grammatically off. I think "To disable," sets up an imperative clause. (The original wording wasn't mine either.) What about just removing the latter part?

To disable Strict Transport Security, set max-age=0.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, or if we did want to keep the second part:

To disable Strict Transport Security, set max-age=0: this will immediately...

- `includeSubDomains` {{optional_inline}}
- : If this optional parameter is specified, this rule applies to all of the site's subdomains as well.
- : If this optional directive is specified, the HSTS policy applies to all subdomains of the host's domain as well.
- `preload` {{optional_inline}} {{non-standard_inline}}
- : See [Preloading Strict Transport Security](#preloading_strict_transport_security) for details. When using `preload`, the `max-age` directive must be at least `31536000` (1 year), and the `includeSubDomains` directive must be present.
Not part of the specification.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Not part of the specification.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with removing it (I didn't write it), but it does seem relevant to me. It is a vendor extension. Is it just considered too much detail?

Perhaps also worth noting for the preload info in this page overall, the https://hstspreload.org/ site itself says in bold, "HSTS preloading is not recommended."

Perhaps on this page it should be more of a footnote, or just link out to that site?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with removing it (I didn't write it), but it does seem relevant to me. It is a vendor extension. Is it just considered too much detail?

Just that it's already indicated, by the little warning icon. This isn't very clear: however, it means we have a defined mechanism for representing nonstandard, which we have tooling to maintain: if it became standardized, we would almost automatically remove the icon, but not any free-form text.

Perhaps also worth noting for the preload info in this page overall, the https://hstspreload.org/ site itself says in bold, "HSTS preloading is not recommended."

That's very interesting, especially the stuff about Chrome/Safari just always upgrading (I use Firefox, which doesn't, so I didn't know this). I do think this should be noted in this page.

### How the browser handles Strict Transport Security

When an HTTPS response includes the `Strict-Transport-Security` header, the browser adds the host's domain name
to its persistent list of non-expired Known HSTS Hosts.
Copy link
Collaborator

@wbamberg wbamberg Jun 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
to its persistent list of non-expired Known HSTS Hosts.
to its list of HSTS hosts.

(here and elsewhere) I think you can simplify this. "Known HSTS Host" is a spec term and this section would make just as much sense if it uses the term "HSTS host". We don't need to say the list is persistent, this is surely obvious. And I'm not sure of the value in talking about a list of "non-expired Known HSTS Hosts", sure if it's expired it's not an HSTS host any more, by definition?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. I tried to represent the RFC accurately, but it may be too precise for a summary page like this. I included "non-expired" specifically in reference to section 8.1, which uses a whole paragraph to say as much about the max-age=0 case. But this doc also covers the case later in the paragraph about expirations, so I agree common sense can suffice.

"Persistent" being obvious I'm not so sure of. It is obvious to us, but I try to think from the perspective of an audience that has never heard of this before. Most HTTP headers are intrinsically stateless, and affect only the current request. I added "persistent" to introduce into the reader's mind the fact that this is a stateful mechanism. I'm still OK with removing it though, if you want, as I agree most people will figure it out.


Strict Transport Security resolves this problem; as long as you've accessed your bank's website once using HTTPS, and the bank's website uses Strict Transport Security, your browser will know to automatically use only HTTPS, which prevents hackers from performing this sort of man-in-the-middle attack.
With HSTS, as long as at least one secure connection has been made to the host in the past and the `Strict-Transport-Security` response header was present, the browser remembers it as a Known HSTS Host, and the connection uses HTTPS before a man-in-the-middle attack has a chance to occur. However, the HSTS header alone cannot protect an initial insecure request if the browser has never before connected to the host and so does not have the domain name in its Known HSTS Hosts list. [Preloading](#preloading_strict_transport_security) can mitigate this problem. For the same reason, it is still important to specify the `https` scheme in URLs even when using HSTS.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With HSTS, as long as at least one secure connection has been made to the host in the past and the Strict-Transport-Security response header was present, the browser remembers it as a Known HSTS Host, and the connection uses HTTPS before a man-in-the-middle attack has a chance to occur.

This is not 100% correct as it doesn't account for HSTS expiry. Also "man"->"manipulator", and "occur" is a very passive way to describe an attack.

Maybe something like:

Suggested change
With HSTS, as long as at least one secure connection has been made to the host in the past and the `Strict-Transport-Security` response header was present, the browser remembers it as a Known HSTS Host, and the connection uses HTTPS before a man-in-the-middle attack has a chance to occur. However, the HSTS header alone cannot protect an initial insecure request if the browser has never before connected to the host and so does not have the domain name in its Known HSTS Hosts list. [Preloading](#preloading_strict_transport_security) can mitigate this problem. For the same reason, it is still important to specify the `https` scheme in URLs even when using HSTS.
Once the browser recognises the host as an HSTS host, then all connections will use HTTPS from the beginning, even if the URL uses the HTTP scheme. This prevents a MITM attack on the initial request.
However, the browser is still vulnerable to a MITM attack if the very first connection uses HTTP, because the browser will not yet have received the `Strict-Transport-Security` header, so it won't recognise the host as an HSTS host. The browser is also vulnerable if the `Strict-Transport-Security` header has expired. [Preloading](#preloading_strict_transport_security) can mitigate this problem. For the same reason, it is still important to specify the `https` scheme in URLs even when using HSTS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good points. It's true that it doesn't account for expiry; I just thought that would be obvious since there is a paragraph about it, and the focus here is on the initial vs. subsequent requests. I can definitely work that in though. I like the brevity of what you wrote, but I do think it misses an essential point: "Once the browser recognises the host" doesn't specify the conditions for that to occur, whereas my versions does. However, I acknowledge it may not be necessary to give that information again here, as it is explained elsewhere on the page.

I think "remember" is more precise than "recognize", because "recognize" doesn't explain how, but "remember" highlights that it's simply state, again emphasizing the statefulness.

But the main thing I'm trying to make clear is the difference between the first ever connection (prior to the browser knowing it is an HSTS host) and subsequent connections. In your wording, the time related words are "from the beginning", "initial", "very first" -- but the last one actually means a different time than the first two.

I'll have to work on this some more later.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a suggestion, but in case it is helpful. I think "Threat models" isn't a great title: for one, it makes less sense if there is only one threat, which there is if we remove the HTTP-subresources threat. It's also very abstract. I would call this section "Mitigating Manipulator-in the Middle" or something, and structure it something like:

  • HTTPS as a whole is an effective protection against MITM
  • sites typically want to benefit from HTTPS while still supporting users who request pages using HTTP
  • to do this they redirect HTTP requests to HTTPS
  • in this situation the first HTTP connection request is still vulnerable to MITM, and this is what HSTS protects against
  • with HSTS, as long as a site is recognized as an HSTS host, then the initial connection is also made over HTTPS
  • there is still a potential vulnerability if the very first connection to a site is made over HTTP, before the site has had a chance to send the STS header (or, similarly, if the STS header has expired and the client connects using HTTP
  • this is what the preload list is designed to address

"Once the browser recognises the host" doesn't specify the conditions for that to occur, whereas my versions does. However, I acknowledge it may not be necessary to give that information again here, as it is explained elsewhere on the page.

No, it doesn't, but as you say, these conditions are already described above.

@hamishwillee
Copy link
Collaborator

hamishwillee commented Jun 12, 2025

I'm removing myself from review - i.e. even though I will remain marked as a reviewer I'm only going to stay here to learn some new things. You two are doing a much better job on this than I would have.

@vobarian
Copy link
Contributor Author

Ready for re-review

Copy link
Collaborator

@wbamberg wbamberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I think this is great! Thanks @vobarian !

@wbamberg wbamberg merged commit 7174049 into mdn:main Jun 19, 2025
8 checks passed
@hamishwillee
Copy link
Collaborator

This is so much better. Thanks you 2!

@vobarian
Copy link
Contributor Author

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Content:HTTP HTTP docs size/m [PR only] 51-500 LoC changed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Known HSTS Hosts are not the same as "sites"
3 participants