HSTS问题本质是浏览器缓存服务端策略后主动拦截HTTP请求,需同步验证浏览器缓存、服务端响应头及配置位置:先查chrome://net-internals确认是否Found;再curl -I检查Strict-Transport-Security头是否仍存在;最后确保Nginx在443 server块中用always参数下发max-age=0并重载,同时手动删除浏览器HSTS记录。

问题本质不是 Nginx 在跳转,而是浏览器已记住 HSTS 策略,主动拦截 HTTP 请求——请求根本没发出去,所以你在 Nginx 日志里查不到 301/302 记录。排查必须从浏览器缓存、服务端响应、配置位置三方面同步验证。
第一步:确认浏览器是否已生效 HSTS
这是最常被跳过的环节,但决定后续所有操作是否必要:
- 在 Chrome 或 Edge 地址栏输入:chrome://net-internals/#hsts
- 在 “Query domain” 输入你的测试域名(如 test.example.com),点击 Query
- 若返回 Found 并显示 max-age 值(如 31536000),说明浏览器已强制 HTTPS;若为 Not found,问题与 HSTS 无关,应检查 301 跳转或证书配置
第二步:检查 Nginx 是否仍在发送非零 HSTS 头
即使你改了配置,旧策略可能因未重载、写错位置或未覆盖所有响应状态而持续下发:
- 执行 curl -I https://test.example.com,观察响应头中是否存在 Strict-Transport-Security,且值不是 max-age=0
- 确保配置只写在 listen 443 ssl 的 server 块内,不能放在 http 块、location 块顶层或 80 端口 server 中
- add_header 指令必须带 always 参数,例如:
add_header Strict-Transport-Security "max-age=0; includeSubDomains" always;
否则 404、500 等非 2xx 响应不会携带该头,浏览器收不到“撤销指令”
第三步:服务端与客户端协同清除
仅改服务端配置不够,浏览器本地缓存需手动干预,二者缺一不可:
- 服务端先部署 max-age=0 配置,并执行 nginx -s reload 重载
- 该响应需持续返回至少原 max-age 时长(如原设 1 年,就要保持 1 年返回 max-age=0),才能让所有已缓存用户逐步失效
- 对当前调试用的浏览器,立即访问 chrome://net-internals/#hsts → “Delete domain security policies” → 输入域名 → Delete
- 若使用 includeSubDomains,删除时需分别删主域和关键子域(如 example.com 和 api.example.com)
特别注意预加载风险
如果你的域名曾提交到 hstspreload.org 并被收录,浏览器启动即硬编码强制 HTTPS,响应头无法撤销:
- 此时只能永久维持全站 HTTPS 可用性,或等待官方列表更新(通常需数月)
- 申请移除需满足:max-age=0 持续满 1 年 + 全域 HTTPS 关闭 + DNS 删除相关记录
- 临时验证可使用无痕窗口(不继承 HSTS 缓存),快速判断是否生效


















