HSTS max-age过大导致浏览器强制HTTPS且无法回退,需先通过chrome://net-internals/#hsts查询域名是否显示Found,再用curl -I确认响应头是否含非零Strict-Transport-Security,最后服务端设max-age=0并手动删除浏览器HSTS记录。

当 Nginx 中 HSTS 的 max-age 设得过大(比如直接设为 31536000 秒即 1 年),一旦配置出错或需临时切回 HTTP,浏览器会强制 HTTPS 重写请求,且不发包——用户打不开网站,也无法通过刷新或清 Cookie 解决。这不是服务端问题,而是浏览器本地策略已“锁死”。排查关键在于分清服务端是否还在推送、客户端是否已缓存、以及如何安全回退。
确认浏览器是否已记录该域名的 HSTS 策略
这是第一步,也是最常被跳过的环节:
- 在 Chrome 或 Edge 地址栏输入:
chrome://net-internals/#hsts - 在 “Query domain” 输入你的测试域名(如
test.example.com),点击 Query - 若返回 Found,并显示
max-age=31536000,说明浏览器已生效;若为 Not found,问题不在 HSTS,应排查跳转逻辑或证书
检查 Nginx 是否仍在发送非零 max-age 的 HSTS 头
即使你改了配置,旧策略仍可能因未重载、配置位置错误或未覆盖所有响应路径而持续下发:
- 执行
curl -I https://test.example.com,查看响应头中是否有Strict-Transport-Security,且值不是max-age=0 - 确保配置写在
listen 443 ssl的server块内,不能放在http或location块顶层(易被覆盖) - 必须带
always参数:例如add_header Strict-Transport-Security "max-age=0; includeSubDomains" always;,否则 404/500 等状态码不会携带该头,浏览器收不到“撤销指令”
服务端与客户端协同清除的正确步骤
仅改服务端不够,浏览器缓存需手动干预,二者缺一不可:
- 服务端先部署
max-age=0配置,并重载 Nginx(nginx -s reload)或更新 Ingress - 该响应需持续返回至少原
max-age时长(如原来是 1 年,就要保持 1 年返回max-age=0,才能让所有已缓存用户逐步失效) - 对当前调试用的浏览器,立即访问
chrome://net-internals/#hsts→ “Delete domain security policies” → 输入域名 → Delete - 若使用子域(
includeSubDomains),删除时需分别删主域和关键子域(如example.com和api.example.com)
避免未来陷入同样困境的操作建议
上线前就控制风险边界,比出事后再救更高效:
- 首次启用 HSTS 必须从短周期起步:
max-age=300(5 分钟),观察 15 分钟以上无跳转异常、混合内容、子域不可达等问题再延长 - 禁用
includeSubDomains直到所有子域(如cdn、api、static)全部稳定支持 HTTPS 且证书有效 - 暂勿启用
preload,它意味着提交至浏览器内置列表,撤回周期以月计,且审核失败后无法快速补救 - 每次修改后,用
curl -I和chrome://net-internals/#hsts双验证,不依赖“看起来能打开”


















