必须在443端口https server块中配置add_header strict-transport-security "max-age=31536000; includesubdomains; preload" always,搭配80端口301跳转,灰度验证后再上线。

在 Nginx 中为 HTTPS 站点开启 HSTS,核心是让浏览器“记住”只用 HTTPS 访问你的域名,从而跳过首次 HTTP 请求这个最易被劫持的环节。它不是跳转指令,而是一把“记忆型安全锁”。配置本身简单,但位置、参数和节奏稍有偏差,就可能导致子域无法访问、测试环境被永久拦截,甚至上线后难以回退。
必须放在 443 端口的 HTTPS server 块中
HSTS 头只在加密连接下生效,浏览器会直接忽略 HTTP(80 端口)响应中的该头。如果错加在 http 块、location 块顶层或 80 端口 server 中,等于没配。
- 正确做法:只在
server { listen 443 ssl; }块内添加 - 禁用全局 http 块或非 HTTPS 上下文中的 add_header 指令
- 若使用反向代理(如 Spring Boot、Node.js),需屏蔽后端返回的 HSTS 头:
proxy_hide_header Strict-Transport-Security;
add_header 必须带 always 参数
不加 always,Nginx 默认只在 2xx 响应中发送该头。一旦遇到 404、500、304 等状态,HSTS 就中断传递,策略可能失效或被浏览器丢弃。
- 标准写法:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; - preload 是可选标记,仅在满足预加载条件并准备提交至 hstspreload.org 时启用
- includeSubDomains 表示所有一级子域(如 api.example.com、shop.example.com)都必须已支持 HTTPS 且证书有效
上线前务必灰度验证
max-age 设太大,出问题后用户端无法快速清除(只能等过期或手动清理浏览器 HSTS 缓存)。生产环境推荐从短周期起步。
- 首次部署建议用
max-age=300(5 分钟),观察 15 分钟以上 - 确认无证书错误、混合内容、子域不可达、跳转异常等问题后再逐步延长
- 用
curl -I https://example.com检查响应头是否含预期值,注意看 status 是否为 200 - Chrome 地址栏输 http:// 会直接拦截不发包,验证跳转逻辑请用隐身窗口或 curl
配套基础必须稳固
HSTS 不是“补丁”,而是 HTTPS 安全闭环的最后一步。它依赖前置条件全部就绪。
- SSL 证书必须有效、受信,且覆盖所有
server_name声明的域名(包括 www 和裸域) - 80 端口必须配置 clean 的 301 跳转:
return 301 https://$host$request_uri;,禁用 rewrite - 检查 CDN(如 Cloudflare)是否透传
Strict-Transport-Security;默认可能过滤自定义头,需手动开启











