引言:为什么不能直接上31536000?
HSTS(HTTP Strict Transport Security)的 max-age 参数决定了浏览器记住"只走HTTPS"指令的时间长度。很多教程一上来就让你设 max-age=31536000(一年),但这其实是个坑。
如果你配错了、或者证书部署有问题,用户一年内都无法访问你的HTTP版本——哪怕你后来修好了。所以正确的做法是两个阶段:先用小值测试,确认无误后再切换到正式值。
HSTS max-age 基础回顾
HSTS响应头格式:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
max-age:单位秒,浏览器缓存这个指令的时间includeSubDomains:子域名也生效preload:申请加入浏览器内置HSTS列表
测试值选择策略
| 阶段 | max-age值 | 时长 | 用途 |
|---|---|---|---|
| 第一轮测试 | 60 | 1分钟 | 快速验证基础功能 |
| 第二轮测试 | 300 | 5分钟 | 验证浏览器行为 |
| 第三轮测试 | 86400 | 1天 | 接近正式环境验证 |
| 正式上线 | 31536000 | 1年 | 生产环境 |
为什么从60秒开始?
60秒足够你完成以下验证:
- 用curl检查响应头是否正确输出
- 用浏览器访问,观察Network面板是否有HSTS头
- 如果配错了,最多等60秒就能重新访问HTTP版本
从测试值切换到正式值的完整步骤
第一步:配置测试值并验证
在Nginx配置中设置测试值:
server {
listen 443 ssl;
server_name example.com;
# HSTS测试值:60秒
add_header Strict-Transport-Security "max-age=60" always;
# 其他SSL配置...
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
}
验证方法:
curl -I https://example.com
# 期望输出包含:
# Strict-Transport-Security: max-age=60
第二步:浏览器实际验证
- 打开Chrome,访问
https://example.com - 按F12打开开发者工具,切到Network面板
- 刷新页面,点击第一个请求,查看Response Headers
- 确认
strict-transport-security: max-age=60存在
然后在Chrome地址栏输入:
chrome://net-internals/#hsts
在"Query HSTS/PKP domain"输入框中输入你的域名,点击Query,确认HSTS策略已生效。
第三步:等待测试值过期,或无视它直接切正式值
方案一:等测试值自然过期(推荐)
等60秒后,Chrome会自动清除这个域名的HSTS缓存。你可以再次访问HTTP版本(如果有的话)验证降级是否正常。
方案二:手动清除HSTS缓存(快速)
在 chrome://net-internals/#hsts 页面,使用"Delete domain security policies"功能删除你的域名,然后直接修改Nginx配置切换正式值。
第四步:切换到正式值
修改Nginx配置:
server {
listen 443 ssl;
server_name example.com;
# HSTS正式值:1年
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
}
重载Nginx:
nginx -t # 先测试配置语法
nginx -s reload # 重载配置
第五步:最终验证
curl -I https://example.com
# 确认输出:Strict-Transport-Security: max-age=31536000; includeSubDomains
# 用Chrome再次访问 chrome://net-internals/#hsts 查询
# 确认max-age显示为31536000
切换时的注意事项和回滚方案
注意事项
always参数很重要:没有always,只有200/300响应码才会带HSTS头。404/500页面不会输出HSTS头,导致浏览器可能不记录HSTS策略。- HTTP站点不能输出HSTS头:HSTS头只能通过HTTPS输出。如果你有HTTP到HTTPS的重定向,确保重定向响应本身不输出HSTS头(某些旧版Nginx配置会这样)。
- 一旦切换正式值,很难回滚:用户浏览器已经记住了"这个域名只走HTTPS",你无法强制他们清除缓存(除非他们手动操作chrome://net-internals)。
回滚方案
如果你已经切换到了31536000,但发现证书有问题需要回滚:
方案A:等一年(不现实)
方案B:让用户手动清除HSTS缓存
在你的网站首页或错误页面,给出清除HSTS缓存的指引:
- 地址栏输入
chrome://net-internals/#hsts - 在"Delete domain security policies"中输入你的域名
- 点击Delete
方案C:提前规划HTTP备用域名
如果你预计可能需要回滚,可以提前准备一个不包含HSTS的备用域名(如 www2.example.com),在紧急情况下将流量切换过去。
验证方法汇总
| 工具 | 命令/操作 | 验证内容 |
|---|---|---|
| curl | curl -I https://域名 | 响应头是否包含HSTS |
| Chrome | chrome://net-internals/#hsts | 查询HSTS策略是否生效 |
| Firefox | 地址栏输入 about:config,搜索 siteSecurity | 查看HSTS缓存条目 |
| 在线工具 | SecurityHeaders.com | 综合评分和HSTS检测 |
| OpenSSL | openssl s_client -connect 域名:443 | 证书链和TLS配置 |
常见错误和解决方案
错误1:HSTS头输出在HTTP响应里
现象:访问 http://example.com,响应头里居然有 Strict-Transport-Security。
原因:Nginx配置里在 server { listen 80; } 块里也写了 add_header Strict-Transport-Security。
解决:HSTS头只能在HTTPS的server块里输出。HTTP的server块只做301跳转到HTTPS即可。
错误2:includeSubDomains导致子域名无法访问
现象:主域名HTTPS正常,但 api.example.com 还是HTTP的,现在无法访问了。
原因:主域名输出了 includeSubDomains,浏览器强制所有子域名也必须走HTTPS。
解决:
- 给子域名也部署HTTPS证书
- 如果子域名暂时无法上HTTPS,需要等max-age过期(或者让用户手动清除HSTS缓存)
错误3:max-age设为0想清除HSTS但无效
现象:把max-age改成0,但浏览器还是强制HTTPS。
原因:max-age=0只能让浏览器"将来不记住HSTS",但已经缓存的HSTS策略不会立刻清除。而且如果之前输出了 preload,浏览器会忽略max-age=0。
解决:通过 chrome://net-internals/#hsts 手动删除HSTS策略。
总结
HSTS max-age的切换不是一件可以"拍脑袋"决定的事情。正确的流程是:
- 测试阶段:用60秒→300秒→86400秒逐步验证
- 验证内容:响应头输出、浏览器行为、证书链完整性、子域名覆盖
- 正式切换:确认无误后,一次性切换到31536000
- 持续监控:用SecurityHeaders.com等工具定期检测
记住:HSTS是一种"单向门",一旦用户浏览器记住了,你就很难回头。所以测试阶段多花点时间是值得的。
相关文章
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论