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.confWordPress 官方伪静态 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