
如果你已经给网站装好了 SSL(安全传输协议)证书,访客却仍然可能通过 http:// 打开旧链接,浏览器地址栏的“不安全”提示就会反复出现,甚至有被中间人降级到明文 HTTP 的风险。为什么装了证书还不能高枕无忧?解决这个问题的关键,就是 HSTS 与预加载列表。这篇指南会教你如何在服务器上正确配置 HSTS 响应头,并逐步过渡到 preload 预加载列表,让浏览器在第一次访问之前就知道你的站点只允许 HTTPS。
HSTS 是什么:用响应头锁死 HTTPS
HSTS(HTTP Strict Transport Security,HTTP 严格安全传输策略)的本质是一个由服务器下发的 HTTP 响应头:Strict-Transport-Security。浏览器收到这个头之后,会在本地记住一条规则——在指定时间内,对该域名的所有请求一律使用 HTTPS,即使你在地址栏手输 http://,浏览器也会在发出请求前自动改写为 https://。
这条机制直接封堵了两类常见问题,也是 服务器运维中高频出现的场景。第一类是“301 跳转依赖”:只靠服务器返回 301 把 HTTP 跳转到 HTTPS 时,第一次请求仍然是明文的,攻击者可以在这一步劫持连接、伪造跳转,这就是经典的 SSL 剥离(SSL Stripping)降级攻击。第二类是混合内容:页面里残留的 http:// 子资源请求会触发浏览器告警,影响信任度。有了 HSTS,浏览器在本地就完成了协议升级,跳转环节被彻底绕开。
一个最小的 HSTS 响应头如下:
Strict-Transport-Security: max-age=31536000
其中 max-age 的单位是秒,31536000 即一年。这个值的含义是“浏览器要记住这条规则多久”,也决定了你后悔的余地——一旦下发,主流浏览器在到期前不会轻易松开,所以配置节奏要稳,下文会给出分阶段方案。
核心参数怎么配:max-age、includeSubDomains 与 preload
完整的 HSTS 头通常由三个部分组成。理解每个参数的边界,比记住整行配置更重要,因为其中任何一个写错,影响范围都可能超出预期。
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
max-age=31536000:规则有效期一年。建议先用max-age=300(5 分钟)或max-age=86400(1 天)试运行,确认全站 HTTPS 无异常后再提升到一年。includeSubDomains:规则覆盖所有子域名。前提是每个子域都支持 HTTPS,包括那些内部 API、测试环境;如果有子域还停在 HTTP,加上这个参数会直接导致它们无法访问。preload:声明你准备申请加入浏览器的预加载列表。它本身不改变浏览器行为,但提交 preload 申请时必须带上这个标记,且max-age不低于 31536000。
值得留意的顺序问题是:includeSubDomains 一旦生效,子域名的证书也要全部就位。如果你的邮件服务、图床或旧系统仍依赖 HTTP,请先为它们补齐证书,或者暂时不加这个参数。可以按“5 分钟 → 1 天 → 1 年 + includeSubDomains → preload”四个阶段逐级推进,每一级都留出观察窗口。

Nginx 配置步骤:在 443 端口下发 HSTS
在 Nginx 上配置 HSTS 的正确位置是监听 443 端口的 server 块,而不是 80 端口。原因前面已经说过:HSTS 头只在 HTTPS 响应中有意义,明文 HTTP 响应中下发的 Strict-Transport-Security 会被浏览器忽略。
server {
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/nginx/ssl/example.com.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# 其他站点配置...
}
server {
listen 80;
server_name www.example.com example.com;
return 301 https://www.example.com$request_uri;
}
这里有两个容易踩的坑。其一,add_header 默认只作用于 2xx 类响应;加上 always 参数才能覆盖 404、502 等错误页,保证任何响应都携带 HSTS 头。其二,如果 server 块里还有其他 add_header(比如安全 CSP),Nginx 的继承规则是“子级出现 add_header 就覆盖父级整组”,所以要么把所有头写在同一层级,要么在每处显式重复 HSTS 那一行。
改完配置后验证并重载:
nginx -t && nginx -s reload curl -sI https://www.example.com | grep -i strict-transport
第二条命令应该输出完整的三段式 HSTS 头。如果没有输出,先检查 always 参数和 server 块层级,再确认 443 端口证书链完整。
Apache 配置步骤:mod_headers 与 VirtualHost
Apache 用户通过 mod_headers 模块实现同样的效果。先确认模块已启用(多数发行版默认开启):
apachectl -M 2>/dev/null | grep headers_module a2enmod headers && systemctl reload apache2
然后在 443 端口的 VirtualHost 中加入:
<VirtualHost *:443>
ServerName www.example.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.com.pem
SSLCertificateKeyFile /etc/ssl/private/example.com.key
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
# 其他站点配置...
</VirtualHost>
与 Nginx 类似,always 关键字保证错误响应也携带该头。如果站点由多个模块分层处理请求(例如 mod_proxy 转发到后端应用),要确认 HSTS 头在最外层 VirtualHost 设置,避免被内层配置覆盖。这些细节与 WordPress 站点维护中排查响应头的思路一致,验证方式同样是 curl -sI 加 grep -i strict-transport。
预加载列表:从“事后记忆”到“事前知晓”
HSTS 仍有一个无法自我解决的盲区:用户第一次访问站点时,浏览器还没有收到过任何响应头,这次初始连接仍可能被降级攻击劫持。preload 预加载列表就是为了补上这个缺口——它是一份内置在 Chrome、Firefox、Safari、Edge 等浏览器中的域名清单,列入其中的站点,浏览器在第一次连接前就会强制使用 HTTPS,完全跳过不安全的首次请求。
申请入口是 hstspreload.org。提交前必须满足几个硬性条件: Serving HSTS on the base domain(主域名本身也要支持 HTTPS)、max-age 至少一年、启用 includeSubDomains、携带 preload 标记,并且整个域名族(含所有子域和通配符覆盖不到的四级域名)都能通过 HTTPS 访问且证书有效。
申请的代价要提前想清楚:
- 加入列表后,所有现代浏览器将在本地内置你的域名,用户无法通过普通设置关闭 HTTPS 访问。
- 子域名必须全部支持 HTTPS,包括未来新增的子域;如果将来某个内部系统只想用 HTTP,会直接不可用。
- 退出列表的周期很长:先修改响应头移除 preload,再提交移除申请,浏览器更新内置清单通常需要数月以上,旧版本浏览器可能永远保留记录。
因此建议的路径是:先在主域稳定运行一年期 HSTS 数周,确认没有任何子域掉队,再提交 preload 申请。对多数中小站点,做到一年期 HSTS + includeSubDomains 已经能覆盖绝大部分风险,preload 是锦上添花而非必选项。

验证与排障:确认配置真的生效
配置完成后,不要只看一次响应头就下结论。建议按下面的顺序做一轮完整验证,每一步都有明确的通过标准。
- 浏览器直检:打开开发者工具的 Network 面板,查看任意文档响应是否包含
strict-transport-security,包括 404 页面。 - 命令行复核:
curl -sI http://www.example.com应返回 301,而curl -sI https://www.example.com应包含完整 HSTS 头。 - 安全扫描:用 securityheaders.com 或类似工具扫一次,确认 HSTS 评级与预期一致。
- preload 预检:在 hstspreload.org 输入域名,按其逐项检查结果修正问题后再正式提交。
如果浏览器行为异常,例如明明配了 HSTS 却仍能打开 HTTP 页面,先清掉本地 HSTS 缓存再测:Chrome 访问 chrome://net-internals/#hsts,在 Delete domain security policies 中输入域名删除,再重新访问验证。另一个高频问题是证书链不完整导致 HSTS 站点直接报错——因为浏览器被锁定只能走 HTTPS,证书问题不再有 HTTP 兜底,建议用 openssl s_client -connect www.example.com:443 -servername www.example.com 检查完整证书链,并确保证书在到期前完成续期。
总结与建议
HSTS 用一个响应头把“HTTP 跳转到 HTTPS”的主动权从服务器转移到了浏览器本地,preload 列表则把保护提前到第一次连接。对刚上 HTTPS 的站点,推荐的落地节奏是:先以 max-age=86400 试运行一周,检查所有子域与混合内容;再提升到一年并按需启用 includeSubDomains;最后评估是否申请 preload。如果你需要一次性把安全头、TLS 配置和跳转策略配齐,也可以考虑在 Hostease 的 VPS(虚拟专用服务器)或 独立服务器上自主部署,按本文步骤操作。配置完成后,建议每季度复查一次证书有效期与子域名 HTTPS 覆盖情况,让强制 HTTPS 长期稳定地跑下去。