nginx中配置hsts必须在443 https server块内使用add_header strict-transport-security "max-age=31536000; includesubdomains; preload" always,且需满足全站https稳定、80端口301跳转、所有子域证书有效、灰度验证后再启用preload。

在 Nginx 中配置 add_header Strict-Transport-Security 开启 HSTS,核心不是“加一行头”,而是确保它在正确上下文中、以正确方式、配合必要前提稳定生效。配置错位置或缺关键参数,浏览器根本不会接受策略。
必须写在 443 HTTPS 的 server 块里
HSTS 头只在加密连接中被浏览器识别,HTTP(80 端口)响应里加了也完全无效,甚至可能干扰调试。常见错误包括:
- 把
add_header放在listen 80的 server 块中 - 写在全局
http块或未启用 SSL 的location里 - 用
if ($scheme = https) { add_header ... }—— Nginx 官方明确不推荐,行为不可靠
正确做法是:只在已启用 SSL 的 443 server 块内添加,且该块必须包含有效的 ssl_certificate 和 ssl_certificate_key 配置。
always 参数不能省
默认情况下,Nginx 只对 2xx/3xx 响应(如 200、301、302)发送 add_header。但浏览器要求 HSTS 策略在所有 HTTPS 响应中持续存在,包括:
- 404 页面(资源不存在)
- 500 错误页(服务异常)
- 304 响应(缓存未变)
- 静态文件、API 接口返回的各类状态码
一旦某次响应缺失 HSTS 头,浏览器可能中断策略记忆,导致安全链断裂。所以必须加上 always:
max-age 和 includeSubDomains 要分步验证
这两个参数影响范围大,上线前必须灰度测试:
- 首次启用建议设为
max-age=300(5 分钟),观察主站和子域是否 HTTPS 正常、无证书错误、无跳转循环 - 确认稳定后,逐步延长至
max-age=31536000(1 年) - 启用
includeSubDomains前,确保www、api、cdn等所有一级子域均能独立 HTTPS 访问,并返回完整 HSTS 头
注意:includeSubDomains 不递归生效,dev.api.example.com 不受保护。
preload 是承诺,不是开关
加上 preload 表示你愿意将域名提交到浏览器预加载列表(hstspreload.org),实现“首次访问即强制 HTTPS”。但它有硬性门槛:
- 全站必须稳定 HTTPS
-
max-age≥ 31536000 - 已启用
includeSubDomains - 80 端口必须 return 301 到 HTTPS,且无任何 HTTP 回退入口
一旦提交成功,撤回需数月甚至半年以上,不可逆。上线前务必完成全部验证。











