·438 字 / 约 1 分钟
Nginx 的 add_header 会「静默失效」,这点很坑
在子 location 里加一行 Cache-Control,父层的安全头全部消失——而且 nginx -t 不会报任何错。
运维Nginx踩坑
现象
给站点配好了一组安全响应头,nginx -t 通过,首页检测也正常。然后为了给静态资源加长缓存,在 location 里写了一行:
location /assets/ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
重新加载后,/assets/ 下的响应头里只剩 Cache-Control。X-Frame-Options、Referrer-Policy、Permissions-Policy 全都不见了。
而首页 / 是正常的。
原因
Nginx 的 add_header 指令遵循一条很容易被忽略的继承规则:
当且仅当当前层级没有任何
add_header指令时,才会继承上一层的add_header。
注意是「任何一个都不继承」——只要子层级写了哪怕一条,父层级的全部 add_header 都会被整体丢弃。这不是覆盖,是清空。
所以上面那段配置的实际语义是:/assets/ 下只输出 Cache-Control。
最容易踩的地方
nginx -t 不会报错,因为语法完全合法。这个 bug 只能靠实际抓响应头发现。
三种典型触发场景:
- 为
/assets/加长缓存 - 为图片/字体目录加缓存
- 为
location = /index.html单独设置不缓存
处理方式
方案一(推荐):父层声明默认值 + 子层完整重复
server {
add_header Strict-Transport-Security "max-age=15552000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
location /assets/ {
add_header Cache-Control "public, max-age=31536000, immutable";
# 下面这五行必须完整重复,少一行就少一个头
add_header Strict-Transport-Security "max-age=15552000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
}
}
方案二:不要为 HTML 单独建 location
如果只是想给 HTML 设 no-cache,把它放在 server 层,然后让 / 的 try_files 回退自然继承即可。多建一个 location = /index.html 反而是给自己挖坑。
教训
配置"看起来生效了"和"真的生效了"是两件事。凡是靠继承规则工作的东西,改动之后都值得抓一次真实响应头来验证——尤其是这类合法但语义意外的配置。