深色模式
Cloudflare WAF 误拦 WordPress REST API:安全放行 Application Password 的正确方法
这次问题发生在我现在这套写作和发布架构里:前台 sirenyan.cn 是部署在 Cloudflare Pages 上的静态站,后端 wp.sirenyan.cn 继续作为 WordPress 内容后台。公开读取的 REST API 用来给静态站同步文章;已认证的 REST API 则用来做自动发文。
这套架构本身很舒服:WordPress 负责写作和保存原始内容,Cloudflare Pages 负责把最终页面静态化展示。但安全规则一收紧,就很容易出现一个细节问题:Cloudflare 在请求到达 WordPress 之前就会先判断、拦截。
故障现象
最开始的规则是为了保护 WordPress REST API:允许公开读取,禁止写入。规则逻辑大致是拦截 /wp-json/ 下所有非 GET、HEAD、OPTIONS 的请求。
这样做看起来合理,因为静态同步只需要读取文章。但实际使用时出现了两个问题:
- 在 WordPress 后台创建 Application 时失败。
- 使用 Application 调用 REST API 发文时返回
403 Forbidden。
也就是说,规则不仅挡住了匿名写入,也把后台操作和已认证的 API 写入一起挡住了。
根因
根因不是 WordPress Application 失效,而是请求根本没有走到 WordPress。Cloudflare WAF 是站在 WordPress 前面的,它会先根据域名、路径、HTTP 方法和请求头判断要不要拦截。
当规则写成“只要是 /wp-json/ 下的非读取请求就拦截”时,Cloudflare 不会等 WordPress 验证用户名和 Application 。请求还没到 WordPress,就已经被挡掉了。
正确策略
更合适的策略是分层处理:
GET、HEAD、OPTIONS继续允许,用于公开读取和静态同步。- 带
Authorization: Basic的写请求放行到 WordPress,由 WordPress 自己验证凭据。 - 其余没有认证头的匿名写请求,继续由 Cloudflare 拦截。
这里要特别注意:允许带 Basic 头的请求到达 WordPress,不等于认证一定成功。错误的用户名或错误的 Application 仍然会被 WordPress 拒绝。Cloudflare 只负责第一层过滤,WordPress 负责真正的身份验证。
最终规则表达式
最终使用并通过 Cloudflare 接受的表达式如下:
txt
(http.host eq "wp.sirenyan.cn" and starts_with(http.request.uri.path, "/wp-json/") and not http.request.method in {"GET" "HEAD" "OPTIONS"} and not any(http.request.headers["authorization"][*] contains "Basic "))这里有一个小细节:Cloudflare Rules language 里的 http.request.headers["authorization"] 是数组。使用 any(...[*] contains ...) 比直接写 [0] 更稳,也更符合它对多值请求头的表达方式。
验证结果
修改后验证了几件事:
- 公开
GET /wp-json/...正常,静态站同步不受影响。 - 匿名
POST /wp-json/...被 Cloudflare 返回403。 - 带有效 Application 的
POST可以成功创建文章。 - 危险入口的拦截规则仍然保留。
- WordPress 登录页和后台仍然保持 Managed Challenge。
- 规则只限定在
wp.sirenyan.cn,不影响sirenyan.cn的 Cloudflare Pages 前台。
安全建议
这种做法的重点不是把 REST API 全部暴露出去,而是让 Cloudflare 和 WordPress 各自负责自己擅长的一层:
- Cloudflare 负责拦截匿名写请求和高风险路径。
- WordPress 负责验证已认证请求的用户名和 Application 。
- 全程必须使用 HTTPS。
- Application 应该单独创建,用完可以随时撤销。
- 不要把 Application 写入日志、文章、代码仓库或公开配置文件。
- 自动化发布时,只把凭据放在安全的运行环境里。
后续整理
这次顺手也把三条自定义规则的显示说明改成了中文,后面维护时更容易看懂。Cloudflare 入口规则集本身的 name 字段属于锁定字段,所以保留英文名称,只把规则说明改成中文。
结论
WordPress REST API 的安全规则不能只按路径和方法一刀切。对于需要自动发布的站点,更合理的做法是:公开读取放行,匿名写入拦截,带认证头的写入交给 WordPress 验证。这样既保留了 Cloudflare 的前置防护,也不会破坏 Application 这种标准认证机制。