预加载,但要慎重

HSTS preload:提交要求、风险,以及如何检查响应头和列表状态

HSTS preload 列表会把你的域名内置到浏览器中,让首次访问也使用 HTTPS,并覆盖所有子域名。加入只需填一份表单,退出却要数月。本文说明确切的提交要求、让它成为单向决定的风险、分阶段提高 max-age 的方案,以及检查响应头和列表状态的命令。

概念插图:一个象牙色陶瓷长柜,抽屉众多;在打开的抽屉里,铜色镊子把一块发着薄荷绿光的玻璃片放进空槽,一道薄荷绿光束把它与身后的玻璃拱门连在一起。
概念插图

HSTS 会告诉浏览器只用 HTTPS 访问你的网站,但前提是浏览器至少收到过一次该响应头。HSTS preload 列表正是为了弥补这个空档。它是内置于 Chrome 的域名列表,Firefox、Safari 和 Edge 的列表也以它为基础,因此这些浏览器对列表中的域名及其所有子域名,会在第一个请求发出之前就改用 HTTPS。

加入列表只需在 hstspreload.org 填一份表单,退出却要数月。本文介绍这个列表是什么、确切的提交要求、让大多数网站难以回头的风险、分阶段提高 max-age 的方案,以及如何检查响应头和列表状态。所有示例都使用 example.com。

2026 年的简要结论:hstspreload.org 建议所有 HTTPS 网站启用 HSTS,但默认已不再建议预加载。在给任何网站添加 preload 指令之前,请先读第 2 节。

本指南的适用范围

本文只讨论从外部可以看到的内容:公开响应头、重定向和公开的 preload 列表。免费检测工具读取一个公开响应,看不到内部子域名,也不是渗透测试。

1. HSTS preload 列表是什么

HTTP 严格传输安全定义于 RFC 6797。服务器通过 HTTPS 发送 Strict-Transport-Security: max-age=…,浏览器会记住在 max-age 秒内该主机只能通过 HTTPS 访问。在访客的浏览器第一次收到该响应头之前,首个明文 http:// 请求没有任何保护,同一网络中的攻击者可以拦截它。RFC 描述了这一引导阶段的弱点,并建议以预先配置的策略作为解决办法之一。

preload 列表就是这一办法的实际落地。Chromium 项目维护该列表,新条目会被硬编码进 Chrome 的源代码。根据 Chromium HSTS 页面和 hstspreload.org,Firefox、Safari 和 Edge 各自维护以 Chrome 列表为基础的列表。在包含该条目的每个浏览器版本中,预加载的域名从首次启动起就只用 HTTPS;由于提交必须包含 includeSubDomains,其下的每个名称也是如此。

有三点常被误解:

  • preload 指令在浏览器中不起任何作用。它不属于 RFC 6797,浏览器会忽略不认识的指令。它只是告诉列表维护者,域名所有者同意被收录。一旦响应头带有它,任何人都可以通过表单提交你的域名。
  • 只有完整的可注册域名才能进入列表。你提交的是 example.com,而不是 www.example.com 或 shop.example.com,条目覆盖所有子域名,包括无法从公网访问的内部子域名。
  • 有些顶级域名整体被预加载。例如 .app 或 .dev 下的所有域名,在使用该列表的浏览器中本来就只用 HTTPS,无论所有者是否提交过。

2. 你还需要预加载吗?

对大多数网站来说,可能不需要。提交网站本身现在就说明:建议使用 HSTS,但不建议预加载。Chrome 和 Safari 会对以 http:// 开头的导航自动尝试 HTTPS,无论 HSTS 策略如何,因此预加载原本要填补的首次访问空档已经缩小。根据 hstspreload.org,只有当这些自动升级因主动攻击者而失败时,预加载才会增加保护,其收益与 HSTS 本身相比微乎其微。

当以下条件全部成立时,预加载仍然可能有意义:

  • 你在意首次访问时的降级攻击,例如用户会在公共网络上登录或付款;
  • 当前和未来的每个子域名,包括内部和第三方托管的子域名,都能用有效证书提供 HTTPS;
  • 你打算长期持有该域名,并多年保持其 HTTPS。

如果有团队或供应商运营着你无法完全掌控的子域名,内部主机放在公开域名之下,域名属于短期活动,或者你可能出售或转让该域名,就应当跳过或推迟:条目会随域名转给下一任所有者,直到有人申请移除。配置良好、不带 preload 的 HSTS 响应头,已经能保护每一位回访用户。

3. 确切的提交要求

hstspreload.org 会在你提交时检查这些条件,之后你还必须持续满足:不再满足的域名可能被移除,而从响应头中去掉 preload 会使域名立即具备被移除的资格。

要求实际含义检查方法
有效证书基础域名提供浏览器信任且证书链完整的证书网站打开时没有警告;curl https://example.com/ 不报证书错误
同一主机上从 HTTP 跳转到 HTTPS如果 80 端口有响应,http://example.com/ 要先重定向到 https://example.com/,而不是直接跳到 https://www.example.com/curl -sS -D - -o /dev/null http://example.com/ 显示 301 或 308 及该 Location
所有子域名使用 HTTPS每个子域名(包括多级和内部子域名)都能通过 HTTPS 工作;如果 www 有 DNS 记录,就需要 HTTPS查看你的 DNS 区域和证书清单;表单看不到内部名称
基础域名上的 HSTS在来自 https://example.com/ 的 HTTPS 响应中发送,包括该处提供的任何重定向curl -sS -D - -o /dev/null https://example.com/
max-age 至少为 31536000即一年的秒数;hstspreload.org 上的示例使用 63072000,即两年查看 max-age= 后的数字
includeSubDomains把策略扩展到所有子域名响应头中包含该指令
preload表示你同意被收录响应头中包含该指令

重定向规则会让许多网站不达标。如果 https://example.com/ 重定向到 https://www.example.com/,那么这个重定向响应本身必须带有完整的响应头。www 页面上的响应头不算数,因为由 www.example.com 设置的策略永远不会作用于基础域名。满足这些要求的响应头如下:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

4. 风险:为什么预加载很难撤销

  • 移除需要数月。移除是一次源代码变更,要随浏览器更新才能到达用户。移除页面说明,到达大多数 Chrome 用户可能需要 6 到 12 周,其他浏览器可能更久。从不更新的浏览器会一直保留旧条目。
  • 无法使用 HTTPS 的子域名会失效。典型情况包括只在内部 DNS 中解析的内网主机、使用自签名证书的打印机、路由器和存储设备的管理页面、被遗忘的预发布服务器,以及通过 CNAME 接入、只响应 HTTP 的第三方服务,例如帮助台、状态页或邮件点击跟踪链接。在预加载了你域名的浏览器中,它们会报错,而且无法绕过。
  • 证书错误会变成硬性失败。对于启用 HSTS 的主机,RFC 6797 要求浏览器在出现任何证书错误时终止连接,不允许访客点击继续。任何子域名上的证书过期,都会让所有人无法访问,直到证书被更换,因此提交前请先自动化续期并监控到期日期。
  • 回访用户会保留已存储的策略。退出列表并不会清除浏览器从你的响应头中学到的策略。如果 max-age 为两年,这份副本就会持续两年,除非访客再次访问并通过 HTTPS 收到 max-age=0。
  • 意外提交。从模板复制的响应头如果已带有 preload,任何人都可以据此提交你的域名。在完成下一节的方案之前,不要加这个指令。

5. 提交前分阶段提高 max-age 的方案

hstspreload.org 建议分阶段提高 max-age,并从第一阶段起就带上 includeSubDomains。每个阶段都要查找出错的页面,并关注流量、登录和收入等数据,修复发现的问题,然后至少等待该阶段完整的 max-age 时长再进入下一阶段。你可以先让一个测试群体运行这些阶段,但之后要对所有用户运行。

阶段响应头取值至少等待关注什么
0. 盘点暂不修改直到清单完整从 DNS 区域、证书申请记录和 Certificate Transparency 日志中找出每个子域名,并逐一测试 HTTPS
1. 五分钟max-age=300; includeSubDomains5 分钟;几天的真实流量能告诉你更多被遗忘子域名上的错误、混合内容
2. 一周max-age=604800; includeSubDomains1 周客服工单、登录和结账错误、内部工具
3. 一个月max-age=2592000; includeSubDomains1 个月月度任务、第三方和邮件链接、很少使用的主机
4. 两年max-age=63072000; includeSubDomains; preload然后提交证书续期和每个新增子域名,只要你还在列表中就要持续关注

在 nginx 中,请在 HTTPS 的 server 块里用 always 参数发送该响应头,让错误页面也带上它,并在同一主机上保留 HTTP 重定向。location 块中的 add_header 行会替换该处的 server 级响应头(nginx headers 模块)。

server {
    listen 80;
    server_name example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com;
    # 第 1 阶段:每个阶段都修改此值
    add_header Strict-Transport-Security "max-age=300; includeSubDomains" always;
}

仅逐步提高数值就要五周多。提交之后,hstspreload.org 表示新条目可能要几个月才能进入稳定版 Chrome,因此预加载绝不是应对上线日期的快速办法。

6. 如何检查你的 HSTS 响应头

在终端中检查三个关键响应:

curl -sS -D - -o /dev/null http://example.com/
curl -sS -D - -o /dev/null https://example.com/
curl -sS -D - -o /dev/null https://www.example.com/

第一个应返回 301 或 308,并带有 Location: https://example.com/;它不需要 HSTS 响应头,因为浏览器会忽略通过明文 HTTP 收到的 HSTS。第二个必须带有包含三个指令的 strict-transport-security,即使它本身是跳转到 www 的重定向。第三个也应发送该响应头,因为只访问 www 的访客永远收不到基础域名的策略。在 Windows 上,请调用 curl.exe 并使用 -o NUL。

注意这些常见错误:

  • 出现两个 HSTS 响应头。当 CDN 和源站都添加了一个时,RFC 6797 规定浏览器只处理第一个,hstspreload.org 的检查会把“Multiple HSTS headers”报告为错误。请确定由哪一层负责该响应头。
  • 指令拼写错误。浏览器会悄悄丢弃不认识的指令,所以写成没有末尾 s 的 includeSubdomain,会让子域名在不知不觉中失去覆盖。
  • 部分响应缺失。首页有该响应头,但错误页面、API 或由另一层提供的静态文件没有。

在 Chrome 开发者工具中打开 Network 面板,待浏览器存储了策略后,在地址栏输入 http://example.com/。第一条记录会显示 307 Internal Redirect 和 Non-Authoritative-Reason: HSTS:浏览器在任何请求到达服务器之前就自行切换到了 HTTPS。MDN 参考文档列出了该响应头的语法。

7. 如何检查 preload 列表状态

  • hstspreload.org。在表单中输入域名。它会显示该域名是已预加载、待处理还是不在列表中,并列出对照当前要求的错误和警告。待处理的域名尚未受到保护:它还必须随浏览器版本发布。
  • chrome://net-internals/#hsts。在 Chrome 中,于“Query HSTS/PKP domain”下输入域名。以 static_ 开头的字段来自该浏览器版本内置的列表;以 dynamic_ 开头的字段来自浏览器收到的响应头。“Delete domain security policies”只会清除动态部分,因此预加载条目会一直保留,直到浏览器更新将其移除。
  • 其他浏览器。Firefox、Safari 和 Edge 按各自的节奏发布从 Chrome 列表派生的副本,因此新增或移除到达它们的时间可能不同。

在 DNS、CDN 或证书发生变更之后,请重复检查。hstspreload.org 指出,不再满足要求的域名将来可能被自动移除。

8. 退出列表:移除步骤与时间

  1. 继续在基础域名上提供带有效证书的 HTTPS;移除表单会检查这一点。
  2. 发送不含 preload 的 HSTS 响应头。hstspreload.org 把这个响应头视为移除请求。如果你仍想使用 HSTS,请保留 includeSubDomains 和较长的 max-age;若要完全关闭 HSTS,则发送 max-age=0。
  3. 在移除表单上提交该域名。
  4. 等待期间,为促使你做出这个决定的子域名配置有效证书,或把它迁移到其他域名:移除到达大多数 Chrome 用户可能需要 6 到 12 周,其他浏览器更久。
  5. 除非想被重新加入,否则不要再次发送 preload。

max-age=0 只在 HTTPS 上有效,而且只对再次访问并收到它的访客生效。请按最慢的情况做打算:仍内置该条目的过时浏览器,或已存储的两年期策略,在你改变方向很久之后仍可能继续强制使用 HTTPS。

9. 免费的上线准备快照能显示什么

免费的上线准备快照无需注册,对一个公开域名运行八项被动检查,并返回总分和每项检查的结果。其中有两项与 HSTS 相关:

  • HTTP 安全响应头检查会在最多五次重定向之后,读取你输入域名的首页。若 HTTPS 目标缺少 Strict-Transport-Security 响应头,会评为中等,并显示它观察到的值。
  • 当你输入的是裸域名或 https:// 地址时,HTTPS 重定向检查会把低于 180 天(15,552,000 秒)的 max-age 标为低风险。在方案的第 1 到 3 阶段,出现这一结果是预期之内的。

它不检查 includeSubDomains、preload 指令、你的子域名或列表状态;这些请使用 hstspreload.org 和上面的 curl 命令。若想先查看响应头结果,请使用免费的安全响应头检测工具,它运行的是同一个快照。证书方面请阅读 TLS 证书与 HSTS 指南,而网站上线检查清单则把 HSTS 与重定向、DNS 及其他上线前检查放在一起。

常见问题

现在还建议使用 HSTS 预加载吗?

默认不建议。hstspreload.org 建议 HTTPS 网站使用 HSTS,但不建议预加载,因为 Chrome 和 Safari 已经会对 http:// 导航尝试 HTTPS。预加载主要在主动攻击者阻止这些升级时才增加保护,而且从列表中移除需要数月。

HSTS preload 列表要求多大的 max-age?

在基础域名的 HTTPS 响应中,至少 31536000 秒(一年),并同时包含 includeSubDomains 和 preload。hstspreload.org 上的示例响应头使用 63072000,即两年。请分阶段达到它:先 300、604800 和 2592000 秒,最后再设为最终值。

可以只预加载 www 或某一个子域名吗?

不可以。列表接收的是可注册域名,例如 example.com,条目覆盖其下的每个子域名,包括内部子域名。只要有一个子域名无法用有效证书提供 HTTPS,就不要预加载该域名。

从 HSTS preload 列表中移除要多久?

hstspreload.org 表示,移除到达大多数 Chrome 用户可能需要 6 到 12 周,其他浏览器可能更久。先从响应头中去掉 preload,再提交移除表单。已存储你响应头的访客会保留该策略,直到其 max-age 到期。

如何检查我的域名是否在 preload 列表中?

在 hstspreload.org 输入域名,它会显示已预加载、待处理或未预加载,以及对照要求的错误。在 Chrome 中,chrome://net-internals/#hsts 会显示来自内置列表的静态条目和从响应头学到的动态条目。

来源与延伸阅读

  1. HSTS Preload 列表提交(要求与建议)hstspreload.org
  2. HSTS Preload 列表移除hstspreload.org
  3. RFC 6797:HTTP 严格传输安全(HSTS)www.rfc-editor.org
  4. MDN:Strict-Transport-Security 响应头developer.mozilla.org
  5. Chromium 项目:HTTP 严格传输安全www.chromium.org
  6. OWASP HTTP 严格传输安全速查表cheatsheetseries.owasp.org
  7. nginx:ngx_http_headers_module(add_header)nginx.org
Sitelemetry 团队

由 Sitelemetry 编辑团队编写。文末的一手资料可帮助你深入了解相关主题。

SITELEMETRY

把学到的知识用起来。

通过 Sitelemetry 探索网站的结构、设置与技术信号。