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 只会对以上这些状态码添加响应头。常见的错误状态码(400、403、404、500 等)不会触发 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-Security、X-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辅助作者原创,未经许可,转载请保留原文链接。

发表评论