WordPress 无法添加应用程序密码?Nginx 规则冲突导致 403 报错排查与修复
一、问题现象
- 前端表现:在 WordPress 后台「个人资料」页面,输入名称点击「添加应用程序密码」时,页面弹出空白红色报错框,无法生成密码。
- 网络请求:按 F12 打开浏览器开发者工具,发现发往以下 API 接口的 POST 请求失败,状态码为 403 Forbidden,且响应内容为 Nginx 原生 HTML 报错页面,说明请求在 Nginx 接入层即被直接拒绝,尚未到达 PHP/WordPress。
二、问题排查与定位思路
1. 从 Nginx 访问日志提取关键线索
查看站点 Nginx 访问日志(/www/wwwlogs/your_domain.log),观察发起操作时的请求记录:
POST /wp-cron.php... HTTP/1.1" 200 ...
GET /wp-admin/profile.php HTTP/1.1" 200 ...
POST /wp-json/wp/v2/users/1/application-passwords?_locale=user HTTP/1.1" 403 548 ...
POST /wp-admin/admin-ajax.php HTTP/1.1" 200 ...
分析思路:
- 同一时间段内,后台页面(profile.php)及 Ajax 接口(admin-ajax.php)均返回 200 OK。
- 唯独向 REST API 路径发起的 POST /wp-json/wp/v2/users/1/application-passwords 请求返回了 403,且响应大小为 548 字节(典型 Nginx 拦截响应体)。
- 结论:服务器配置了专门针对 /wp-json/wp/v2/users/ 路径或 REST API 的规则拦截。
2. 从主 Nginx 配置文件追溯保护文件位置
排查站点主配置文件(/www/server/panel/vhost/nginx/your_domain.conf),主配置并未直接写入 deny 语句,但发现通过 include 引入了外部安全扩展目录:
# 主 Nginx 配置文件中引用的扩展规则位置
include /www/server/panel/vhost/nginx/extension/your_domain/*.conf;
深入此目录检查关联配置文件,发现了导致问题的防用户枚举安全加固规则:
# 外部扩展文件位置:/www/server/panel/vhost/nginx/extension/your_domain/security.conf
# 拦截用户列表根路径
location = /wp-json/wp/v2/users {
return 403;
}
# 强行拦截所有子路径(导致故障的根源)
location ^~ /wp-json/wp/v2/users/ {
return 403;
}
三、根本原因分析
- ^~ 修饰符覆盖了放行规则:在 Nginx 匹配逻辑中,^~ 代表「只要匹配到此前缀,立即停止检索后续的所有正则表达式」。这导致即便在配置中添加了放行密码 API 的正则,也会被该规则无条件截断并直接返回 403。
- 缺少伪静态路由:若仅仅对 API 路径进行放行而未配置 try_files,Nginx 会尝试在服务器磁盘寻找真实目录或静态文件,进而导致 REST API 报 404 Not Found。
四、关键路径参考表
| 类别 | 详细路径参考 | 说明 |
|---|---|---|
| 受影响的 REST API | /wp-json/wp/v2/users/<用户ID>/application-passwords | 生成应用程序密码必须调用的 API |
| 站点主配置文件 | /www/server/panel/vhost/nginx/your_domain.conf | 包含全局 rewrite 和 include 指令 |
| 安全加固配置文件 | /www/server/panel/vhost/nginx/extension/your_domain/*.conf | 存放 OpenClaw / WAF 安全加固规则 |
| 伪静态规则配置文件 | /www/server/panel/vhost/rewrite/your_domain.conf | WordPress 官方伪静态 include 文件 |
| Nginx 访问日志 | /www/wwwlogs/your_domain.log | 排查状态码与请求路径的日志位置 |
五、最终解决方案
编辑安全加固配置文件 /www/server/panel/vhost/nginx/extension/your_domain/security.conf,修改为以下既能防御用户枚举、又能正常生成密码的兼顾配置:
# 1. 正则放行「生成应用程序密码」API,并交给 WordPress 伪静态处理
location ~* /wp-json/wp/v2/users/\d+/application-passwords {
try_files $uri $uri/ /index.php?$args;
}
# 2. 精确拦截用户列表根路径(防止未授权扫描用户)
location = /wp-json/wp/v2/users {
return 403;
}
# 3. 拦截其他用户枚举请求(去掉 ^~,允许上面的正则第 1 条优先生效)
location /wp-json/wp/v2/users/ {
return 403;
}
# 基础安全响应头
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 "geolocation=(), microphone=(), camera=()" always;
修改完成后,执行以下命令重载配置即可生效:
nginx -t && nginx -s reload