nginx中必须用add_header而非header set配置hsts,因后者是apache指令,nginx不识别会导致nginx -t报错;add_header需置于listen 443 ssl的server块内并加always参数,且需注意location块中不继承、避免覆盖。

不能用 Header set 配置 HSTS —— Nginx 没有这个指令,必须用 add_header,且仅限 HTTPS server 块中生效。
为什么 Header set 在 Nginx 中根本不存在
Nginx 官方文档和所有稳定版本中,Header set 是 Apache 的语法(对应 Header always set),不是 Nginx 的指令。你在 Nginx 配置里写 Header set Strict-Transport-Security ... 会导致 nginx -t 直接报错:unknown directive "Header"。
正确且唯一可用的指令是 add_header,它属于 ngx_http_headers_module(默认内置,无需额外加载)。
- 错误示例:
Header always set Strict-Transport-Security "max-age=31536000"→ 语法错误,Nginx 启动失败 - 正确写法:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always - 注意:
always参数不是可选修饰,而是关键保障——没有它,404、500、304 等响应不会携带 HSTS 头
必须只在监听 443 的 HTTPS server 块中配置
HSTS 响应头只有在 HTTPS 连接中被浏览器接收才有效。RFC 6797 明确规定:HTTP 响应中的 Strict-Transport-Security 头会被浏览器完全忽略。
- ❌ 错误:在
listen 80的 server 块里加add_header→ 浏览器收不到,还可能暴露你启用了 HSTS 的意图 - ✅ 正确:只出现在
listen 443 ssl的 server 块内,且该块证书必须有效、域名匹配server_name - ⚠️ 注意:如果用了 CDN(如 Cloudflare),需确认它未剥离或覆盖该 header;部分 CDN 要在控制台单独开启 HSTS 开关
add_header 的作用域和继承陷阱
add_header 不会自动继承到 location 块中——这是最常踩的坑。如果你把 HSTS 放在 server 级,但某些 location(比如 PHP 处理块)里又定义了其他 add_header,server 级的头会被完全覆盖(Nginx 的设计如此)。
- 常见失效场景:PHP-FPM 的
location ~ \.php$里没重复写add_header,结果 .php 文件响应里就没有 HSTS 头 - 安全做法:把
add_header放在server块顶层,并确保所有子location都不重定义同名 header;或统一放在http块(但仅当所有虚拟主机都走 HTTPS 且策略一致时才可行) - 验证方式:
curl -I https://yoursite.com/test.php和curl -I https://yoursite.com/都要看到Strict-Transport-Security头
上线前必须验证的三件事
HSTS 一旦被浏览器接受(尤其带 preload),回退成本极高。上线不是配完就完事。
- 先用小
max-age=300(5 分钟)部署,等几个小时后用curl -I https://yoursite.com确认头存在且值正确 - 用 Chrome 访问
chrome://net-internals/#hsts,手动查询域名是否已缓存,再点 “Delete domain” 清除本地测试记录 - 禁用浏览器缓存后,用隐身窗口访问
http://yoursite.com→ 应触发 301 跳转(非 HSTS 行为);之后再访问一次,才体现 HSTS 效果(地址栏自动变 HTTPS,无网络请求发出)
真正危险的不是配错,而是配对了却忘了所有子域都得 HTTPS 可用——includeSubDomains 一开,api.yoursite.com 若证书无效或没配 HTTPS,整个子域将无法访问。











