跳到主要内容
返回笔记
·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-ControlX-Frame-OptionsReferrer-PolicyPermissions-Policy 全都不见了。

而首页 / 是正常的。

原因

Nginx 的 add_header 指令遵循一条很容易被忽略的继承规则:

当且仅当当前层级没有任何 add_header 指令时,才会继承上一层的 add_header

注意是「任何一个都不继承」——只要子层级写了哪怕一条,父层级的全部 add_header 都会被整体丢弃。这不是覆盖,是清空。

所以上面那段配置的实际语义是:/assets/输出 Cache-Control

最容易踩的地方

nginx -t 不会报错,因为语法完全合法。这个 bug 只能靠实际抓响应头发现。

三种典型触发场景:

  1. /assets/ 加长缓存
  2. 为图片/字体目录加缓存
  3. 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 反而是给自己挖坑。

教训

配置"看起来生效了"和"真的生效了"是两件事。凡是靠继承规则工作的东西,改动之后都值得抓一次真实响应头来验证——尤其是这类合法但语义意外的配置。

吾栖星球

个人非经营性网站。记录个人在软件开发、技术学习过程中的笔记与心得,并展示个人独立开发的非商业性小工具与作品集。

已记录 3 篇笔记 · 1 个作品

© 2026 吾栖星球 · 个人非经营性网站 · 自 2026

本站内容仅供学习交流,不构成任何商业服务冀ICP备2021005084号

这里是我的个人网站,非经营性、不提供任何商业服务,纯粹作为学习交流与自我沉淀的空间。