正确配置HSTS需在443 SSL server块中使用add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always,配合80端口301跳转、分阶段灰度测试、CDN透传且不缓存,并确保全站HTTPS稳定运行。

在 Linux 系统上为 Nginx 正确配置 HSTS(HTTP Strict Transport Security)响应头,不是加一行指令就能完事的事。它本质是一份浏览器强制执行的安全承诺,配置不当可能直接导致子域不可访问、用户被锁死在 HTTPS 或预加载失败后无法撤回。核心原则是:只在 HTTPS 上生效、覆盖所有状态码、匹配真实基础设施、分阶段上线。
必须放在 443 server 块内,且带 always 参数
HSTS 头仅通过加密连接传递,HTTP(80 端口)响应中设置完全无效,浏览器会直接忽略。更关键的是,Nginx 默认的 add_header 只对 2xx 成功响应生效。这意味着如果返回 404 页面或 500 错误,HSTS 头就丢失了——策略出现断层,浏览器无法持续记住强制 HTTPS。
- ✅ 正确写法:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;,放在server { listen 443 ssl; ... }块内,SSL 证书配置下方即可 - ❌ 错误写法:写在
http { }全局块里(可能导致 HTTP 站点误带 HSTS)、写在 80 端口 server 块中、或漏掉always - 若后端应用(如 Spring Boot)也输出 HSTS,需用
proxy_hide_header Strict-Transport-Security;屏蔽,避免重复或冲突
max-age 要分阶段验证,慎用 includeSubDomains 和 preload
HSTS 的 max-age 是浏览器本地缓存策略的时间,一旦生效,用户后续所有 HTTP 请求都会被浏览器自动改写为 HTTPS,且不发包。这意味着配置错误将造成真实访问中断。
- 首次上线务必从
max-age=300(5 分钟)开始灰度,观察至少 15 分钟:确认主站和所有子域(如api.example.com、cdn.example.com)都能稳定 HTTPS 访问、无混合内容警告、证书有效 -
includeSubDomains表示策略扩展到所有一级子域。任一子域 HTTPS 不可用,用户访问该子域失败后,主域也可能被浏览器标记为“不安全”并拒绝加载 -
preload是向浏览器预加载列表(如 Chrome 的 hstspreload.org)提交的承诺,不可逆。必须同时满足:max-age ≥ 31536000、含includeSubDomains、所有子域响应头中也有完整 HSTS、全站无 HTTP 回退路径。提交前请先在 hstspreload.org 自检
必须配合 80 端口 301 跳转,堵住首次请求漏洞
HSTS 解决的是“第二次及以后访问”的降级风险,但首次 HTTP 请求仍可能被中间人劫持(如 SSLstrip 攻击)。因此,HSTS 必须与明文端口跳转协同使用,形成闭环。
- 单独配置一个
server { listen 80; return 301 https://$host$request_uri; } - 确保 CDN(如 Cloudflare、腾讯云 EdgeOne)透传
Strict-Transport-Security头,而非缓存或过滤。部分 CDN 默认屏蔽该头,需在控制台手动开启“自定义响应头透传” - 禁止 CDN 缓存带 HSTS 的响应本身。HSTS 是行为指令,不是资源内容;应配置缓存键包含
$host和$ssl_server_name,避免跨域名污染
安全性评估要点:不只是看头是否存在
验证 HSTS 是否真正起效,不能只检查响应头是否出现。要关注三个维度:
-
作用域正确性:用
curl -I https://example.com检查响应头;再试curl -I https://api.example.com(如有子域),确认各自策略独立且合理 -
状态码全覆盖:分别请求一个正常页面、一个 404 路径、一个 500 错误页,确认每种响应都携带 HSTS 头(重点验证
always是否生效) -
实际行为验证:在浏览器中访问
http://example.com,确认是否自动跳转至 HTTPS;打开开发者工具 → Application → Clear storage → 清除 HSTS 缓存后重试,观察首次 HTTP 是否仍被拦截(说明 HSTS 尚未生效或 max-age 过短)











