Nginx 的 add_header 不继承、不叠加、不传递,仅生效于当前 location 块;子 location 写任意 add_header 会丢弃上级同名头;同一 location 内重复设置同名头以最后一条为准;internal 重定向后原 location 的 add_header 失效;错误响应需加 always 参数才生效。

nginx 的 add_header 在多级 location 里不继承、不叠加、不传递——它只认当前 location 块里明确写的那几条,别的全不管。这个行为看似简单,实际一不留神就丢安全头、跨域失效、错误页没 HSTS。
子 location 一写 add_header,上级所有同名头全消失
这是最常踩的坑:只要某个 location 里出现任意一条 add_header,它就自动放弃继承 server 或 http 块中定义的同名响应头。
http { add_header X-Content-Type-Options "nosniff"; }-
server { ... }(没写add_header) location /api/ { add_header Cache-Control "no-store"; }
访问 /api/ 时,X-Content-Type-Options 就不会出现在响应头里——不是被覆盖,是压根不带。想保留,就得在 /api/ 里手动重写一遍:
location /api/ {
add_header X-Content-Type-Options "nosniff";
add_header Cache-Control "no-store";
}
同一 location 内重复写,后写的覆盖前写的
同一个 location 块里多次设置相同 header 名,Nginx 只留最后一个生效的值,不会拼接也不会报错。
add_header X-Robots-Tag "noindex";add_header X-Robots-Tag "noarchive";
最终响应头里只有 X-Robots-Tag: noarchive。如果真要同时生效,得用不同 header 名,或改用 proxy_hide_header + proxy_pass_header 配合上游服务控制。
internal 重定向后,原 location 的 add_header 彻底作废
用 try_files、rewrite ... last、error_page 或 @named location 触发的 internal 跳转,会完全丢弃源 location 的 add_header,只看目标 location 有没有显式配置。
location / { try_files $uri @backend; }location @backend { internal; add_header X-Source "upstream"; }
最终响应头里只有 X-Source: upstream,主 location 中的所有 add_header 全部无效。关键点:必须在终点 location 里重新写全所需 header。
4xx/5xx 错误响应默认不加头,always 是保命开关
add_header 默认只对 2xx 和 3xx 响应生效。遇到 404、500、403 这类错误时,你配的安全头(比如 Strict-Transport-Security、X-Frame-Options)一个都不会发出去——攻击者可能专门触发错误路径来绕过防护。
- 错的:
add_header X-Frame-Options "DENY";→ 500 页面不带该头 - 对的:
add_header X-Frame-Options "DENY" always;→ 所有状态码都带
安全类响应头建议统一加 always,尤其是 HSTS、X-Frame-Options、Content-Security-Policy 等,避免被绕过。


















