目录
- add_header继承规则是什么
- 为什么子级location响应头会丢失
- always参数的作用与陷阱
- 实战:4种常见继承场景
- add_header_inherit新指令(1.29.3+)
- 最佳实践与避坑清单
一、add_header继承规则是什么
Nginx的add_header指令有一条非常关键的继承规则,官方文档原文是:
These directives are inherited from the previous configuration level if and only if there are no add_header directives defined on the current level.
翻译过来就是:只有当当前配置层级没有定义任何add_header指令时,才会从上一级继承add_header。
这条规则看似简单,实际配置中踩坑的人不计其数。核心要点:
- 继承是"全有或全无",不是追加合并
- 只要当前层写了一个add_header,上级的所有add_header全部失效
- if块中的add_header也会触发继承中断
二、为什么子级location响应头会丢失
这是最常见的问题场景。看下面这个配置:
server {
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
location /api/ {
add_header Cache-Control no-cache;
# X-Frame-Options 和 X-Content-Type-Options 丢失了!
}
}
在/api/这个location里,你只会看到Cache-Control响应头,X-Frame-Options和X-Content-Type-Options都不见了。
原因:/api/中定义了add_header,触发了继承中断,server层的两个add_header被完全丢弃。
正确的写法
server {
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
location /api/ {
add_header X-Frame-Options DENY; # 必须重复写
add_header X-Content-Type-Options nosniff;
add_header Cache-Control no-cache;
}
}
或者把公共header提到http层,用include文件统一管理。
三、always参数的作用与陷阱
always参数让add_header在所有响应码下都生效,而不只是200/201/204/206/301/302/303/304/307/308。
add_header X-Frame-Options DENY always;
注意陷阱:always参数本身不影响继承规则。但很多人以为加了always就能解决继承丢失,这是误解。继承丢失的本质是层级定义问题,和always无关。
always解决的另一个问题:404、500等错误页面也会带上安全响应头。如果你的server层定义了带always的安全头,子location没有重复定义,错误页面的安全头也会丢失。
四、实战:4种常见继承场景
场景1:server层定义,location无add_header
server {
add_header Strict-Transport-Security "max-age=31536000" always;
location / {
# 正常继承,HSTS响应头存在
}
}
✅ 继承生效,没有问题。
场景2:server层定义,location也有add_header
server {
add_header X-Frame-Options DENY;
location /download/ {
add_header Content-Disposition "attachment";
# X-Frame-Options 丢失!
}
}
❌ 继承中断,必须在location中重复写X-Frame-Options。
场景3:if块中的add_header
server {
add_header X-Frame-Options DENY;
location / {
if ($scheme = http) {
add_header X-Redirect-Reason "http-to-https";
# 这里的add_header也会中断外层继承
}
}
}
❌ if块是location的子层级,if中的add_header会阻断location层的继承。这是最容易踩的坑。
场景4:include文件的继承
# /etc/nginx/security-headers.conf
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
# server配置
server {
include /etc/nginx/security-headers.conf;
location /api/ {
include /etc/nginx/security-headers.conf; # 必须再次include
add_header Cache-Control no-cache;
}
}
✅ 用include统一管理,每个需要的层级都include一遍,避免遗漏。
五、add_header_inherit新指令(1.29.3+)
Nginx 1.29.3版本新增了add_header_inherit指令,终于从官方层面解决了继承痛点:
add_header_inherit merge; # 合并上级和当前层的add_header
三个参数:
- on(默认):标准继承规则,即当前层有add_header就不继承上级
- off:完全不继承上级的add_header
- merge:合并上级和当前层的add_header,这才是大家想要的
http {
add_header_inherit merge; # 全局开启合并模式
server {
add_header X-Frame-Options DENY;
location /api/ {
add_header Cache-Control no-cache;
# 现在两个header都有!
}
}
}
注意:add_header_inherit本身也是可继承的。在http层设置merge后,所有下级层级默认都是merge模式,除非你手动覆盖。
六、最佳实践与避坑清单
- 公共header用include管理:把安全响应头写在一个conf文件里,每个层级include一遍
- 每个location都检查header是否完整:用
curl -I验证,不要想当然 - 安全响应头加always:X-Frame-Options、HSTS等必须加always,确保错误页也有保护
- 升级Nginx到1.29.3+用merge模式:一劳永逸解决继承问题
- if块中慎用add_header:if块会中断继承,尽量用map指令替代
- 不要在proxy_pass的location中忘记重复安全头:反向代理场景最容易遗漏
add_header继承规则虽然设计上有争议,但理解了"当前层有定义就不继承"这条核心规则,配合include或新版merge指令,配置起来就不会再踩坑。
相关文章推荐
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论