OpenResty/Nginx:HSTS(Strict-Transport-Security)响应头排查与治理指南

适用场景

当站点已删除 vhost 中的 HSTS 配置,但客户端仍持续收到:

Strict-Transport-Security: max-age=31536000

并表现为“浏览器持续强制 HTTPS”或“清理 HSTS 后再次访问又被重新写入”。

本文给出一套可复用的排查与治理流程,适用于 OpenResty/Nginx 反向代理与非反向代理两类站点。

现象确认(以响应头为准)

优先用命令确认是否仍在下发 HSTS:

curl -kI https://192.168.xxx.xxx:443/ | sed -n '1,40p'

若输出包含:

strict-transport-security: max-age=31536000

则可确定:HSTS 来源在服务端链路中(并非浏览器“自动开启”)。

背景机制(为何“删掉一处配置”仍可能存在)

  1. HSTS 仅在 HTTPS 响应中生效;浏览器一旦接收到该头,会按 max-age 缓存。
  2. HSTS 以 Host 为维度缓存(不是以端口为维度)。
  3. 站点层删除 HSTS 配置,不等价于整条链路不再下发该头:
    • 可能存在 include 模板配置
    • 可能存在 Nginx/OpenResty 全局 http {} 配置
    • 可能来自上游应用响应头,并被代理层透传

常见来源与定位顺序

1) include 进来的站点模板(高概率)

典型表现:vhost 中存在类似:

include /www/sites/xxx/proxy/*.conf;

某个被 include 的文件中仍配置:

add_header Strict-Transport-Security "max-age=31536000" always;

2) OpenResty/Nginx 全局层面(http{})统一加固

某些面板/模板会在全局启用 HSTS,使所有站点都带该头。

3) 上游应用自行返回 HSTS

当 OpenResty/Nginx 作为反向代理时,上游应用可能直接返回 HSTS。
若代理层未主动处理,该头会被原样转发给客户端。

根治方案(推荐):从“实际生效配置”定位源头

在服务器上展开最终生效配置并搜索关键字:

openresty -T 2>&1 | grep -in "strict-transport-security"

若使用 nginx:

nginx -T 2>&1 | grep -in "strict-transport-security"

命令输出会给出“文件路径 + 行号”,据此删除或调整对应的 add_header Strict-Transport-Security ...,再 reload。

说明:该方法用于定位 OpenResty/Nginx 配置侧的来源。若输出为空,但响应仍有 HSTS,则更可能来自上游应用响应头。

治理方案(站点层可控):屏蔽上游并清理客户端历史 HSTS

当无法立即修改全局模板、或不确定上游是否会继续下发 HSTS 时,可在站点层做“控制权收敛”,确保最终由 OpenResty 决定是否开启 HSTS。

建议配置:

proxy_hide_header Strict-Transport-Security;
add_header Strict-Transport-Security "max-age=0" always;

配置含义

  • proxy_hide_header Strict-Transport-Security;
    • 屏蔽上游应用返回的 HSTS 响应头,避免上游影响客户端策略。
  • add_header Strict-Transport-Security "max-age=0" always;
    • 通过 HTTPS 响应向客户端下发“清理指令”,将该 host 的 HSTS 记录清空;always 确保 4xx/5xx 等响应也携带该头。

放置位置(关键)

  • 若站点存在 proxy_pass(反向代理):
    • proxy_hide_header 应放在发生反代的 location 作用域内,或确保可覆盖到所有反代 location
  • 若站点为纯静态/直出:
    • proxy_hide_header 无意义,可仅保留 add_header ... max-age=0 always; 用于清理客户端历史记录。

验证步骤

1) 服务端验证

再次查看响应头:

curl -kI https://192.168.xxx.xxx:443/ | sed -n '1,40p'

期望:

  • 不再出现 strict-transport-security
  • 或仅出现 strict-transport-security: max-age=0

2) 客户端验证

若客户端此前已缓存 HSTS(max-age 较大),建议删除浏览器侧记录后复测:

  • Chrome/Edge:chrome://net-internals/#hsts 删除对应 host(示例:192.168.xxx.xxx

注意事项

  • 关闭 HSTS 会降低安全性。对公网站点,需评估是否允许降级攻击风险。
  • 某些环境下 error_page 497 https://$host$request_uri; 会导致“HTTP 误打到 SSL 端口时自动跳转 HTTPS”,其表现可能与 HSTS 类似,但原理不同。
  • 若 OpenResty 前还有更外层的网关/CDN/WAF 且其追加 HSTS,OpenResty 无法删除“外层后来添加”的响应头,需要在外层关闭。

回滚与启用策略

  • 如需重新启用 HSTS:
    • 删除 max-age=0 清理策略
    • 在期望层级(站点或全局)配置目标策略(例如 max-age=31536000
    • 使用 curl -kI 复核响应头