做网站安全检测时,很多人的站点会收到这样一份报告:Content-Security-Policy、X-Frame-Options、X-Content-Type-Options、Referrer-Policy、Permissions-Policy 全部缺失,只有一项 Strict-Transport-Security 已配置。本文记录一次完整修复过程,环境为宝塔面板 + Nginx + WordPress + 腾讯 EdgeOne CDN,同款建站方案可直接参考。
修复前先做了一个对照实验,用三种方式抓取响应头:
# 1. 经 CDN 的线上请求
curl -sSI https://blog.example.com/
# 2. 绕过 CDN 直连源站
curl -sSIk --resolve blog.example.com:443:127.0.0.1 https://blog.example.com/
# 3. 模拟蜘蛛 UA
curl -sSI -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" https://blog.example.com/三种视角结果完全一致 → 说明问题出在源站 nginx 配置,而不是 CDN 层;同时蜘蛛看到的和检测工具看到的是同一份响应头,修好源站,两边同时生效。
宝塔生成的站点配置里通常只有 HSTS 一行。在 /www/server/panel/vhost/nginx/站点.conf 的 #SSL-END 之后加上:
# ===== 安全响应头 =====
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), browsing-topics=() always;
add_header Content-Security-Policy "default-src 'self'; script-src self 'unsafe-inline 'unsafe-eval https://sdk.51.la https://hm.baidu.com https://res.wx.qq.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data: https:; connect-src 'self' https://sdk.51.la https://hm.baidu.com; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'" always;同时把已有的 HSTS 行补上 always:
add_header Strict-Transport-Security "max-age=31536000" always;四个容易踩的坑:
#SSL-END 之后。宝塔面板修改证书/SSL 设置时会整段重写 SSL-START/SSL-END 之间的内容,放在里面的自定义配置会被静默覆盖——这就是很多人加过头,某天又没了"的原因。always 参数。不加它,404 等错误页响应不带头,部分检测工具会判定配置不完整"。'unsafe-inline'。WP 主题和后台大量使用内联脚本/样式,去掉会直接白屏;此场景下 CSP 的主要价值来自 frame-ancestors、base-uri、object-src、form-action 这几条。改完执行:
nginx -t && nginx -s reload修复过程中发现 http://站点 返回的不是 301,而是 200——会产生 http/https 双份重复内容,影响收录。
根因:配置里明明有标准的跳转规则:
if ($server_port !~ 443) {
rewrite ^(/.*)$ https://$host$1 permanent;
}但 EdgeOne 回源使用的是 HTTPS,源站看到的端口永远是 443,规则永远不触发。只有配了 CDN 的站点会遇到这个问题,直连源站测试是正常的,非常具有迷惑性。
解决方法是依据 CDN 透传的真实协议判断:
# CDN回源为HTTPS,依据X-Forwarded-Proto识别访客真实协议
if ($http_x_forwarded_proto = "http") {
return 301 https://$host$request_uri;
}验证(带随机参数穿透 CDN 缓存):
curl -sSI "http://站点/?t=$(date +%s)" | head -3 # 应返回 301
curl -sS -o /dev/null -w '%{http_code}
' "https://站点/?t=$(date +%s)" # 应返回 200,确认无回环检测顺手发现 robots.txt 声明了两个 Sitemap 地址:
sitemap_index.xml → 404(照抄网上模板,站上根本没装 Yoast)wp-sitemap.xml → 200,但内容是空的 urlset(被第三方站点地图插件接管后未生成内容)真正有效的是 sitemap.xml:插件生成的索引 + 10 个子地图全部 200,覆盖全部文章、页面、分类、标签。
# 快速验证 sitemap 是否真的有货
curl -sS https://站点/sitemap.xml | grep -oE <loc>[^<]+</loc>'声明一个 404 或空内容的 Sitemap,蜘蛛会白跑一趟,定期自查很有必要。
源站验证全部通过后,线上检测依然只有旧响应头——因为 EdgeOne 把整个 HTML 响应(连同响应头)缓存了 30 天(Age: 247866,EO-Cache-Status: HIT)。
必须到 EdgeOne 控制台刷新缓存,URL 级刷新首页即可。验证方法是带随机 query 穿透缓存,能看到 EO-Cache-Status: MISS 且新头齐全:
curl -sSI "https://站点/?verify=$(date +%s)" | grep -Ei 'content-security|x-frame|x-content|referrer|permissions|strict-transport'另外建议在 EdgeOne 开启"强制 HTTPS 跳转",与源站 301 形成双保险。
always,位于 #SSL-END 之外nginx -t 通过后 reload最终检测结果:Content-Security-Policy、X-Frame-Options、X-Content-Type-Options、Referrer-Policy、Strict-Transport-Security、Permissions-Policy 六项全部已配置。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。