Web 服务器
开启 HSTS(HTTP 严格传输安全)
开启 HSTS 就是在 HTTPS 响应里加 Strict-Transport-Security 头,浏览器收到后在 max-age 内只用 HTTPS 访问网站。先用 300 秒的 max-age 上线,确认没问题后分阶段提高到一到两年,再考虑 includeSubDomains 和预加载。
配置写法覆盖 Nginx、Apache 2.4、Caddy 2、IIS 10 和宝塔面板。
前提条件
HSTS 会去掉退回 HTTP 的余地,所以 HTTPS 必须先稳定。
- 证书有效且已配置自动续期(见 证书自动续期)。
- 已经配置 HTTP 跳转 HTTPS(见 HTTP 跳转 HTTPS)。
- 没有混合内容;打算加
includeSubDomains的,所有子域名都已支持 HTTPS。
第一步:用很短的 max-age 加上响应头
各服务器都是在 HTTPS 站点里加同一个响应头,max-age 先设 300 秒。
Nginx,写在 listen 443 ssl 的 server 块里:
add_header Strict-Transport-Security "max-age=300" always;
location 块里只要写了自己的 add_header,就不会继承上层的 add_header,需要在这些 location 里再写一遍。
Apache,写在 *:443 虚拟主机里(需要 a2enmod headers):
Header always set Strict-Transport-Security "max-age=300"
Caddy,写在站点块里:
header Strict-Transport-Security "max-age=300"
IIS,写在网站的 web.config 里:
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Strict-Transport-Security" value="max-age=300" />
</customHeaders>
</httpProtocol>
</system.webServer>
改完后重载服务。
第二步:分阶段提高 max-age
max-age 按 5 分钟、1 天、1 周、1 年、2 年逐级提高,每一级观察几天没问题再往上调:
max-age=300 (5 分钟)
max-age=86400 (1 天)
max-age=604800 (1 周)
max-age=31536000 (1 年)
max-age=63072000 (2 年)
第三步:加上 includeSubDomains
确认每个子域名都能提供 HTTPS 后,把策略扩展到子域名:
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
第四步(可选):申请预加载
想被内置到浏览器里,主域名要返回不少于 max-age=31536000 并带 includeSubDomains; preload 的头,证书有效,并且在同一主机上先从 HTTP 跳到 HTTPS,再跳到其他地址:
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
然后到 hstspreload.org 提交。提交前先看清楚那里关于移除的说明。
验证
配置生效后,HTTPS 响应里能看到 Strict-Transport-Security 头。检查线上的响应头:
curl -sI https://example.com | grep -i strict-transport-security
HSTS 检查 还会列出预加载条件是否满足。再用 SSL 证书检查 确认背后的证书,或在终端运行:
curl ssl.sh/example.com
自动续期
HSTS 会把证书问题直接变成网站打不开:证书过期或不匹配时,浏览器显示的错误页没有“继续访问”的选项,所以续期必须可靠。使用每天至少运行一次、支持 ARI 的 ACME 客户端,续期后重载服务,并用 ssl.sh 监控(开发中) 盯住线上证书。证书有效期在缩短:2026 年 3 月 15 日起最长 200 天,Let's Encrypt 默认证书 2027 年 2 月 10 日起 64 天,2029 年 3 月 15 日起最长 47 天。
常见问题排查
- 部分页面没有 HSTS 头:Nginx 某个
location写了自己的add_header导致不继承,或者没加always,错误页面不带这个头。 - 证书出错时用户无法跳过:这正是 HSTS 的作用,修好证书,见 HSTS 无法跳过 和 NET::ERR_CERT_DATE_INVALID。
- 加 includeSubDomains 后某个内网子域名打不开:给它配置有效证书,或在主域名上把 max-age 改为 0,等浏览器忘掉策略。
- 在 HTTP 响应里发送了 HSTS 头:无害但会被忽略,浏览器只接受 HTTPS 响应里的 HSTS。
- 预加载提交被拒绝:hstspreload.org 的检查结果会列出不满足的条件,常见的是主域名没有跳转或 max-age 不足一年。
常见问题
开了 HSTS 想关掉怎么办?
通过 HTTPS 发送 Strict-Transport-Security: max-age=0。浏览器下次看到这个头时才会忘记策略,不再访问的用户要等原来的 max-age 到期。已经预加载的域名还要在 hstspreload.org 申请移除,进入浏览器正式版本需要几个月。
includeSubDomains 安全吗?
只有所有子域名都能提供有效 HTTPS 时才安全,包括 oa.example.com 这类内网系统、自带 Web 管理界面的设备。加之前用 CT 证书查询 和 DNS 记录盘点一遍子域名。
宝塔面板怎么加 HSTS?
宝塔网站设置的 SSL 页面在部分版本里有 HSTS 开关(以面板实际界面为准);没有开关时,在网站的“配置文件”里 443 的 server 块中加 add_header Strict-Transport-Security "max-age=300" always;,保存后面板会自动重载 Nginx。