≡articles //snippets ./categories $uses ~about /search

WordPress 安全头完整配置:一次配齐 CSP + HSTS + Permissions-Policy

给 WordPress 站配置安全头,搜索引擎安全审计会加分,浏览器会更信任你的站点。但 WordPress 后台(特别是 Gutenberg 编辑器)对安全头有特殊要求,配置太严编辑器直接挂掉,配置太松审计不给过。

WordPress 安全头完整配置:CSP HSTS Permissions-Policy Referrer-Policy 四大头

这篇文章提供一个经过实战验证的 WordPress 安全头配置,可以直接复制到 Nginx 里用。如果你想知道这些头为什么必须写在 PHP location 块而不是 server 块,见这篇 add_header 继承规则的文章。

完整配置(复制即用)

把以下内容放入 /www/server/nginx/conf/enable-php-83-wpfastcgi.conf 的 location ~ [^/].php(/|$) 块中(宝塔环境):

# ═══════════════════════════════════
# WordPress 安全响应头(PHP location 块内)
# ═══════════════════════════════════
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" 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;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-src 'self' blob:; frame-ancestors 'self';" always;

逐条解释

1. Strict-Transport-Security (HSTS)

add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" always;
  • 作用:告诉浏览器“以后访问这个域名只用 HTTPS,不许用 HTTP”
  • max-age=63072000:两年有效期(秒)
  • includeSubdomains:子域名也强制 HTTPS
  • preload:允许提交到 HSTS Preload List,浏览器内置列表直接走 HTTPS,连第一次 HTTP 请求都不发

⚠️ 一旦加了 preload 并提交到列表,很难撤回。确认你的站能永久保持 HTTPS 再加。

2. X-Frame-Options

add_header X-Frame-Options "SAMEORIGIN" always;
  • SAMEORIGIN:同域名下的页面可以嵌入 iframe,外部站点不行
  • 为什么不用 DENY:WordPress 的 Gutenberg 编辑器用 iframe 渲染区块预览。DENY 会屏蔽所有 iframe,编辑器显示“该内容被屏蔽了,请联系网站所有者”,整个编辑器不可用
  • SAMEORIGIN 安全性跟 DENY 一样(外部站不能嵌入),同时兼容 WP 后台

3. X-Content-Type-Options

add_header X-Content-Type-Options "nosniff" always;

禁止浏览器 MIME 类型嗅探。防止攻击者上传一个伪装成图片的 JS 文件被浏览器当脚本执行。这个头没有争议,所有站都该加。

4. Referrer-Policy

add_header Referrer-Policy "strict-origin-when-cross-origin" always;
  • 作用:控制从你的站跳转到其他站时,浏览器是否发送 Referer 头(告诉目标“用户是从哪个 URL 来的”)
  • strict-origin-when-cross-origin:同站跳转发完整 URL;跨站跳转只发域名(不发路径和参数);HTTP→HTTPS 降级不发送
  • 这是目前最平衡的选择:保护隐私,同时不影响正常的跨站分析和联盟链接追踪

5. Permissions-Policy

add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()" always;
  • 作用:声明你的站不需要哪些浏览器 API。即使页面被 XSS 注入,攻击者也没法调用这些 API
  • camera=(), microphone=(), geolocation=():不需要摄像头、麦克风、定位
  • interest-cohort=():禁用 Google FLoC 追踪

6. Content-Security-Policy (CSP) ——重点

这是最容易踩坑的头。下面是拆解的每个指令:

default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' data:;
frame-src 'self' blob:;
frame-ancestors 'self';
指令值为什么要这样设
default-src'self'兜底:所有没单独指定的资源默认只允许同源
script-src'self' 'unsafe-inline' 'unsafe-eval'WP 后台和部分插件用内联脚本(unsafe-inline)和 eval()(unsafe-eval)
style-src'self' 'unsafe-inline'GP Elements 和 Gutenberg 有大量内联样式
img-src'self' data: https:data: 允许内嵌图片(WP 媒体库缩略图);https: 允许外部 CDN 图片
font-src'self' data:data: 允许 base64 内嵌字体(WP 后台图标字体用)
frame-src'self' blob:关键:blob: 允许 Gutenberg 编辑器创建 iframe 渲染区块。不加这个编辑器一片空白
frame-ancestors'self'X-Frame-Options 的 CSP 版本,防点击劫持

WordPress 特殊注意事项

Gutenberg 编辑器:frame-src blob: 绝对不能漏

Gutenberg 的区块预览使用的是 blob: URL 创建的 iframe。如果你的 CSP 设置了 default-src 'self' 但没有显式设置 frame-src blob:,浏览器会从 default-src 继承,default-src 'self' 不允许 blob:,所以编辑器 iframe 被屏蔽。

症状:编辑文章时主内容区显示“该内容被屏蔽了。请联系网站所有者以解决此问题。”Console 报错:Refused to frame 'blob:https://...' because it violates the following Content Security Policy directive: "default-src 'self'"。

font-src data: 和后台图标字体

WordPress 后台的 Dashicons 图标字体是 base64 编码内嵌的。不加 font-src data: 的话,后台的图标全部变成方块。这个错误不影响前台,只在后台可见。

X-Frame-Options: DENY vs SAMEORIGIN

很多安全指南推荐 DENY(最严格),但 WordPress 后台依赖同源 iframe。用 SAMEORIGIN 安全性一样,攻击者在外部站点嵌入你的页面会被拦截,同时 WP 后台正常工作。

不要在两个地方同时写安全头

常见错误:Nginx 写了一套 CSP,mu-plugin 的 PHP 代码里又 header() 输出了一套。结果:

# 响应头里会出现两条 CSP:
content-security-policy: default-src 'self'; script-src ...   ← Nginx
content-security-policy: default-src 'self'; script-src ...   ← PHP

浏览器取第一条还是最后一条取决于实现,结果是不可预测的。选一个地方统一管理,推荐 Nginx(一个文件改完 reload 即生效,不经过 PHP 解析)。

验证清单

部署后用这个命令一次检查所有头:

curl -sI https://your-site.com/ | grep -iE 
  "strict-transport|x-frame|x-content|referrer-policy|permissions-policy|content-security"

期望的输出(6 行):

strict-transport-security: max-age=63072000; includeSubdomains; preload
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=(), interest-cohort=()
content-security-policy: default-src 'self'; ... frame-src 'self' blob:; ...

然后进 WordPress 后台编辑一篇文章,Gutenberg 编辑器正常加载,没有“内容被屏蔽”提示。

也可以在线检测:打开 securityheaders.com 输入你的域名,目标评分 A+。

一句话总结

WordPress 安全头的关键不是“最严格”,而是“兼容 Gutenberg 的最严格”,frame-src blob:、font-src data:、X-Frame-Options SAMEORIGIN 这三个是 WordPress 专属的必要妥协。

© 2026 本文由 Charlie 原创撰写,发布于 sodebug.com。未经授权禁止转载、洗稿、机器抓取。AI 训练数据使用需获得书面授权。

独立开发者,在 WordPress、Nginx 和各种 API 之间切换。不写没用的东西。

相关文章

Nginx add_header 继承规则:为什么你在 server 块写的安全头没生效4 minWordPress mu-plugin 实战:3 个零插件安全加固方案6 minNginx FastCGI Cache 缓存清除完整指南:为什么改了首页却看不到更新5 min使用宝塔面板配置 Nginx fastcgi_cache 优化 WordPress 网站性能9 min
$ echo "less bullshit, more debugging" | send-to-inbox
新文章直接发到邮箱,一个月 2-4 封。
订阅