默认情况下add_header不对304响应生效,因其响应体为空且仅作协商确认;需添加always参数才能强制注入,如add_header X-Frame-Options "DENY" always;。

默认情况下,add_header 不会对 304 响应生效,因为 Nginx 只在特定成功或重定向状态码(如 200、204、301–304 等)中添加头,但 304 是个例外:它被明确排除在默认生效范围之外。即使文档列出 304,实际行为是——除非显式加 always 参数,否则 304 响应不会携带任何 add_header 定义的字段。
为什么 304 默认不带自定义头
Nginx 的设计逻辑是:304 响应体为空,仅作协商确认,因此默认跳过所有 add_header 指令,避免冗余或语义冲突。这意味着像 X-Content-Type-Options、Strict-Transport-Security 或 Referrer-Policy 这类安全头,在 304 响应中会完全缺失,可能被浏览器视为“未声明策略”,带来潜在风险。
用 always 参数强制注入 304 响应
只需在 add_header 指令末尾加上 always,即可覆盖状态码限制,确保该头出现在所有响应中,包括 304、404、500 等任意状态码:
add_header X-Frame-Options "DENY" always;add_header Content-Security-Policy "default-src 'self'" always;add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
注意:always 必须写在值之后、分号之前,且不能省略分号;多个头需分别加 always,不可共用。
location 级别配置更安全可靠
避免在 http 或 server 块顶层大量使用 always,以防对非预期路径(如健康检查接口、错误页)也注入安全头。推荐按资源类型精细控制:
- 静态资源(CSS/JS/图片)通常走缓存协商,304 频繁,适合加
always - API 接口若返回 304(较少见),也建议统一加
always保持策略一致 - 错误页面(4xx/5xx)一般不需要安全头,可不加
always或单独处理
示例:
location ~* \.(js|css|png|jpg|gif|woff2?)$ {expires 1y;
add_header Cache-Control "public, immutable" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
}
验证是否生效
用 curl 触发 304 协商并检查响应头:
curl -I -H "If-None-Match: \"xyz\"" http://your-site.com/style.css观察输出中是否包含你配置的自定义头。若无,检查:
— 是否漏写 always
— 是否被下级 location 中的其他 add_header 覆盖(Nginx 不继承,有则屏蔽上级)
— 是否模块未启用(确认编译了 ngx_http_headers_module,通常默认开启)


















