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

在 Nginx 中配置 HSTS 不是加一行响应头就完事,关键在于位置精准、参数严谨、前置条件完备。它真正起效的前提是:你的站点已稳定运行 HTTPS,80 端口已做 301 跳转,所有涉及的子域都有有效证书,并且配置必须落在正确的上下文中。
必须写在 443 HTTPS 的 server 块里
HSTS 头只在 HTTPS 响应中被浏览器接受,HTTP(80 端口)响应里设置完全无效,甚至可能引发误用或策略污染。
- 只允许写在
listen 443 ssl的server块内,且该块已正确配置ssl_certificate和ssl_certificate_key - 禁止写在 80 端口的
server块、http全局块、或未启用 SSL 的location中 - 避免用
if ($scheme = https) { add_header ... }—— Nginx 官方明确不推荐,行为不可靠
务必加上 always 参数
默认情况下,Nginx 只对 2xx/3xx 响应添加 header。但 404、500、304 等状态码若缺失 HSTS 头,浏览器会中断策略记忆,导致安全链断裂。
- 错误写法:
add_header Strict-Transport-Security "max-age=31536000";(缺always) - 正确写法:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; - 加了
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 + 所有子域 HTTPS + max-age ≥ 31536000 + includeSubDomains 已启用 + 无 HTTP 回退入口
- 建议先灰度测试至少 15 分钟,再正式启用;生产环境上线前务必屏蔽后端应用(如 Spring Boot、Node.js)自行返回的 HSTS 头,防止冲突











