0

Nginx add_header always参数作用详解:为什么安全头在错误页面消失了?

2026.05.27 | youres | 8次围观

问题现象:安全头在错误页面消失了

很多人在 Nginx 里配置了 HSTS、X-Frame-Options 等安全响应头,用 curl 访问正常页面时一切正常,但一旦访问一个不存在的 URL,返回 404 时,这些安全头全部消失了

这不是 Nginx 的 bug,而是 add_header 指令的默认行为

add_header 的默认生效条件

Nginx 官方文档对 add_header 的说明里有一句话:

Adds the specified field to a response header provided that the response code equals 200, 201, 204, 206, 301, 302, 303, 304, 307, or 308.

也就是说,默认情况下 add_header 只会对以上这些状态码添加响应头。常见的错误状态码(400403404500 等)不会触发 add_header

这就解释了为什么你的 HSTS 头在 404 页面上不见了。

always 参数:让响应头无条件生效

Nginx 从 1.7.5 版本开始,为 add_header 增加了一个 always 参数:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;

加上 always 之后,无论响应状态码是什么(包括 4xx、5xx),Nginx 都会添加这个响应头。

实战配置示例

配置前(有问题的写法)

server {
    listen 443 ssl;
    server_name example.com;

    # 安全头 — 404页面不会发送!
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    location / {
        root /var/www/html;
    }
}

配置后(正确写法)

server {
    listen 443 ssl;
    server_name example.com;

    # 安全头 — 所有响应状态码都发送
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    location / {
        root /var/www/html;
    }
}

如何验证是否生效

用 curl 分别测试正常页面和错误页面:

# 测试正常页面(200)
curl -I https://example.com/

# 测试404页面
curl -I https://example.com/this-page-does-not-exist

# 测试403页面(如果有目录浏览限制)
curl -I https://example.com/protected/

两次请求都应该能看到 Strict-Transport-SecurityX-Frame-Options 等安全头。如果 404 时没有,说明 always 参数没有加。

add_header 的继承规则(重要)

还有一个容易踩的坑:Nginx 的 add_header 在同一层级内是累加的,但子层级如果重新定义了 add_header,会覆盖父层级的所有 add_header

server {
    # 父层级定义了安全头
    add_header X-Frame-Options "SAMEORIGIN" always;

    location /api/ {
        # 子层级重新定义 add_header,会导致 X-Frame-Options 丢失!
        add_header Cache-Control "no-cache";
        ...
    }
}

解决方法:在子层级里把需要的安全头也加上,或者使用 Nginx 1.29.3+ 的 add_header_inherit merge 指令。

总结

  • add_header 默认只对 200/201/204/206/301/302/303/304/307/308 生效
  • 错误页面(4xx/5xx)默认不会带上这些响应头
  • 加上 always 参数可以让响应头对所有状态码生效
  • 配置安全头时,务必加上 always,否则 HSTS 等策略在错误页面上会失效
  • 注意子层级 add_header 会覆盖父层级的配置,需要手动合并

配置完之后,用 nginx -t 检查配置,然后 nginx -s reload 重载,再用 curl 验证一遍,确保万无一失。

版权声明

本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论
882文章数 0评论数
作者其它文章