0

Nginx HSTS max-age测试值和正式值切换方案:从测试到上线的完整指南

2026.05.27 | youres | 9次围观

引言:为什么不能直接上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值时长用途
第一轮测试601分钟快速验证基础功能
第二轮测试3005分钟验证浏览器行为
第三轮测试864001天接近正式环境验证
正式上线315360001年生产环境

为什么从60秒开始?

60秒足够你完成以下验证:

  1. 用curl检查响应头是否正确输出
  2. 用浏览器访问,观察Network面板是否有HSTS头
  3. 如果配错了,最多等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

第二步:浏览器实际验证

  1. 打开Chrome,访问 https://example.com
  2. 按F12打开开发者工具,切到Network面板
  3. 刷新页面,点击第一个请求,查看Response Headers
  4. 确认 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

切换时的注意事项和回滚方案

注意事项

  1. always参数很重要:没有always,只有200/300响应码才会带HSTS头。404/500页面不会输出HSTS头,导致浏览器可能不记录HSTS策略。
  2. HTTP站点不能输出HSTS头:HSTS头只能通过HTTPS输出。如果你有HTTP到HTTPS的重定向,确保重定向响应本身不输出HSTS头(某些旧版Nginx配置会这样)。
  3. 一旦切换正式值,很难回滚:用户浏览器已经记住了"这个域名只走HTTPS",你无法强制他们清除缓存(除非他们手动操作chrome://net-internals)。

回滚方案

如果你已经切换到了31536000,但发现证书有问题需要回滚:

方案A:等一年(不现实)

方案B:让用户手动清除HSTS缓存

在你的网站首页或错误页面,给出清除HSTS缓存的指引:

  1. 地址栏输入 chrome://net-internals/#hsts
  2. 在"Delete domain security policies"中输入你的域名
  3. 点击Delete

方案C:提前规划HTTP备用域名

如果你预计可能需要回滚,可以提前准备一个不包含HSTS的备用域名(如 www2.example.com),在紧急情况下将流量切换过去。

验证方法汇总

工具命令/操作验证内容
curlcurl -I https://域名响应头是否包含HSTS
Chromechrome://net-internals/#hsts查询HSTS策略是否生效
Firefox地址栏输入 about:config,搜索 siteSecurity查看HSTS缓存条目
在线工具SecurityHeaders.com综合评分和HSTS检测
OpenSSLopenssl 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。

解决

  1. 给子域名也部署HTTPS证书
  2. 如果子域名暂时无法上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的切换不是一件可以"拍脑袋"决定的事情。正确的流程是:

  1. 测试阶段:用60秒→300秒→86400秒逐步验证
  2. 验证内容:响应头输出、浏览器行为、证书链完整性、子域名覆盖
  3. 正式切换:确认无误后,一次性切换到31536000
  4. 持续监控:用SecurityHeaders.com等工具定期检测

记住:HSTS是一种"单向门",一旦用户浏览器记住了,你就很难回头。所以测试阶段多花点时间是值得的。

相关文章

版权声明

本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论
883文章数 0评论数
作者其它文章