安全头配置后为什么检测工具还是报缺失?
我按照文档在 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;
先确认几件事:改完配置有没有 nginx -s reload?有没有 CDN 或反向代理在前面把头给吞了?用 curl -I 你的域名 直接看实际返回,别光看配置文件。
如果确实是继承问题,把所有 add_header 统一放到一个地方,或者在需要的地方都补全。也可以用 include 的方式把安全头抽成一个文件到处引用:
然后在 server 或 location 里 include 这个文件。
另外你说得对,meta 标签只对 CSP 和少数几个生效,X-Content-Type-Options、Strict-Transport-Security 这些必须服务端配,没商量。
第一个坑是 Nginx 的
add_header在有多个层级(比如server和location同时定义)时会覆盖,而不是合并。比如你可能在某个location块里加了别的add_header,结果把前面的全吃掉了——Nginx 的设计就是“谁最后定义就只用谁的”,哪怕前面加了always也没用。更好的写法是把所有安全头集中到一个地方,比如
server块里统一加,或者用include引入一个头文件避免重复定义。比如:第二个坑是 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 吃了。