安全头配置后为什么检测工具还是报缺失?

极客爱香 阅读 13

我按照文档在 Nginx 里加了 CSP、X-Frame-Options 这些安全头,本地测试 headers 看起来都返回了,但用 SecurityHeaders.com 检测还是说 Missing 或低分,到底哪里没配对?

前端这边也试过在 HTML 里加 meta 标签,但好像 meta 不支持所有安全头,比如 Content-Security-Policy 能生效,但 X-Content-Type-Options 就不行。是不是必须全靠服务端?

这是我在 Nginx 配的片段:

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline';" always;
我来解答 赞 1 收藏
二维码
手机扫码查看
2 条解答
Zz智玲
Zz智玲 Lv1
你这个问题八成是 Nginx 的 add_header 继承坑。Nginx 的 add_header 不会自动合并,如果你在 server 块配了,某个 location 块里又配了别的 add_header,那 server 级别的就全丢了。

先确认几件事:改完配置有没有 nginx -s reload?有没有 CDN 或反向代理在前面把头给吞了?用 curl -I 你的域名 直接看实际返回,别光看配置文件。

如果确实是继承问题,把所有 add_header 统一放到一个地方,或者在需要的地方都补全。也可以用 include 的方式把安全头抽成一个文件到处引用:

# security-headers.conf
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline';" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;


然后在 server 或 location 里 include 这个文件。

另外你说得对,meta 标签只对 CSP 和少数几个生效,X-Content-Type-Options、Strict-Transport-Security 这些必须服务端配,没商量。
点赞
2026-03-02 16:09
西门海燕
你这个配置本身没问题,但 SecurityHeaders.com 报 Missing,大概率是以下几种情况之一:

第一个坑是 Nginx 的 add_header 在有多个层级(比如 serverlocation 同时定义)时会覆盖,而不是合并。比如你可能在某个 location 块里加了别的 add_header,结果把前面的全吃掉了——Nginx 的设计就是“谁最后定义就只用谁的”,哪怕前面加了 always 也没用。

更好的写法是把所有安全头集中到一个地方,比如 server 块里统一加,或者用 include 引入一个头文件避免重复定义。比如:

server {
# ... 其他配置 ...

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline';" always;

location / {
# 不要在这里再加 add_header!
try_files $uri $uri/ =404;
}
}


第二个坑是 CSP 的值里有分号和空格,Nginx 配置里如果用双引号包裹,但你又在字符串里写了单引号,某些老版本 Nginx 可能解析出错(虽然 1.6+ 基本没问题,但别赌)。建议用单引号包外面,双引号包里面,或者转义一下,比如:

add_header Content-Security-Policy 'default-src '"'"'self'"'"'; script-src '"'"'self'"'"' '"'"'unsafe-inline'"'"';' always;

不过更推荐直接写成单引号开头结尾,中间内容用双引号,或者干脆去掉单引号(如果不需要严格 CSP):

add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline';" always;

这个写法在大多数新版 Nginx 没问题,但你可以用 curl -I https://your-domain.com 实际看下响应头有没有被正确返回——有时候不是没配,是缓存了旧配置没重载。

第三个容易被忽略的点是:SecurityHeaders.com 默认只检测 / 路径,如果你的首页是重定向(比如 301 到 /index.html),它可能检测的是重定向后的页面,而你只在原路径加了头。记得也给重定向目标加头,或者用 add_header 放在 http 层(但不推荐,影响面太大)。

另外补充一点:X-Frame-Options 用 SAMEORIGIN 是对的,但如果你用了 iframe 且嵌套来源是同源子域名,可能得用 ALLOW-FROM https://example.com(不过这个早过时了,现在 CSP 的 frame-ancestors 更推荐)。不过 SecurityHeaders.com 对 X-Frame-Options 评分影响不大,主要看 CSP 和 HSTS。

最后,别用 meta 标签配 X-Content-Type-Options、X-Frame-Options 这类——它们确实不生效,浏览器只认 HTTP 响应头。CSP 的 meta 标签虽然能用,但只支持部分指令,而且会覆盖服务端 CSP(顺序是 meta > header),容易引发冲突,不建议混用。

总结下排查步骤:

1. 用 curl -I 确认响应头确实带了所有字段
2. 检查有没有多个 add_header 覆盖问题
3. 确保检测的 URL 不是重定向后的路径
4. Nginx 配置改完记得 nginx -t && nginx -s reload,别光 reload 不重载配置文件

实在不行把完整 server 块贴出来,我帮你看看是不是被哪个 location 把 header 吃了。
点赞 1
2026-02-26 14:02