← 返回首页

WordPress REST API 认证调试踩坑复盘

wordpresswp-rest-apiauthenticationdebugtroubleshootingwordpress安全

WordPress 主题/插件开发,或者想用 Headless 架构跑 WordPress,REST API 认证是绕不过去的第一道坎。我在 2026 年 7 月给 TechPassive 博客加 JSON API 时,连踩 5 个坑才调通。本文把每个错误的根因 + 修复命令/配置逐个说透,保证你看完就能解决自己的问题。

前置说明:本文实验环境 WordPress 6.8 + PHP 8.2 + Apache 2.4,命令在 Ubuntu 24.04 LTS 下验证通过。不同主机商环境可能有差异,请先在本地或 staging 站测试。

---

🏗️ 常见认证方式一览(先搞清你用的是哪种)

WordPress REST API 支持 3 种认证:

认证方式适用场景复杂度
**Cookie 认证**(Nonce)同站前端 JavaScript 发起请求⭐ 最低
**Application Passwords**第三方工具/脚本/Headless 前后端分离⭐⭐ 中等
**JWT(插件)**跨域 API 调用,需要额外装插件⭐⭐⭐ 高

搞清楚你用哪种,再去对应章节找解法。

---

💣 踩坑案例 1:Nonce 验证失败 — `rest_cookie_auth_fail`

报错日志(Chrome DevTools Console):

wp-json/wp/v2/posts?status=draft
Headers: X-WP-Nonce: 
Response 401: {"code":"rest_cookie_auth_fail","message":"Cookie nonce is invalid"}

原因

同源前端 JS 发 REST 请求时,WordPress 要求先通过 wp_localize_script 注入 nonce,否则所有写操作(POST/PUT/DELETE)全部 401。我在实际项目中遇到这种情况最多的场景是:用 React/Vue 做前端、WordPress 做后端时,前端发 POST 请求创建草稿文章,结果 nonce 验证失败,一度以为是 JWT 的问题。

解决方案

**Step 1:在 functions.php 中注册 nonce**

add_action('rest_api_init', function () {
    wp_enqueue_script('wp-api');
    wp_add_inline_script('wp-api', '
        wp.apiFetch.use(function(options, next) {
            options.headers = options.headers || {};
            options.headers["X-WP-Nonce"] = wpApiSettings.nonce;
            return next(options);
        });
    ');
});

// 或者更简单:直接暴露 nonce 给 JS
add_action('wp_enqueue_scripts', function () {
    wp_localize_script('jquery', 'wpApiSettings', [
        'root'  => esc_url_raw(rest_url()),
        'nonce' => wp_create_nonce('wp_rest')
    ]);
});

Step 2:前端请求时携带 nonce

// jQuery 方式(老项目)
$.ajax({
    url: wpApiSettings.root + 'wp/v2/posts',
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-WP-Nonce': wpApiSettings.nonce  // 关键这行
    },
    data: JSON.stringify({ title: '新文章', status: 'draft' })
});

// 原生 fetch(现代浏览器推荐)
fetch(wpApiSettings.root + 'wp/v2/posts', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-WP-Nonce': wpApiSettings.nonce
    },
    body: JSON.stringify({ title: '新文章', status: 'draft' })
});

**验证**:登录状态下,浏览器 Console 执行 console.log(wpApiSettings.nonce) 应该输出一串字符,非空。如果输出 undefined,说明 wp_localize_script 没有正确执行。

---

💣 踩坑案例 2:Application Passwords — 密码正确但 403

报错日志(Postman/cURL):

curl -X GET https://yoursite.com/wp-json/wp/v2/posts \
  -u "username:xxxx xxxx xxxx xxxx xxxx xxxx" \
  # -u 是 HTTP Basic Auth 格式
curl: (22) The requested URL returned error: 403

原因

Application Passwords 需要在 WordPress 后台开启,且分配的账号本身有对应权限。2026 年后新装 WordPress 默认关闭(旧版升级上来的需要手动开)。我实际遇到的情况是:生成了 Application Password,但用的是 Subscriber 账号去请求,发文章当然 403——这不是密码错,是权限错。

解决方案

Step 1:确认 REST API 已开启

# 用 WP-CLI 检查
wp option get permalink_structure
# 输出必须是 non-empty(如 /%postname%/)
# 如果是 empty,先改固定链接
wp option update permalink_structure '/%postname%/'
wp rewrite flush

Step 2:开启 Application Passwords(wp-config.php)

// 在 wp-config.php 的 /* That's all, stop editing! */ 之前添加
define('WP_REST_API_ENABLE', true);

Step 3:生成 Application Password

WordPress 后台 → 用户 → 个人资料 → Application Passwords
→ 输入名称(如 "Postman-Dev")→ 生成
→ 复制那串密码(格式:xxxx xxxx xxxx xxxx)

Step 4:验证连通性(排除网络问题)

# 先测试读(不需要认证)
curl -s https://yoursite.com/wp-json/wp/v2/users/me | jq

# 如果返回 401,说明网络/SSL没问题,是认证问题
# 如果返回 403,说明用户名对但密码错
curl -s -u "username:xxxx xxxx xxxx xxxx xxxx xxxx" \
  https://yoursite.com/wp-json/wp/v2/users/me | jq

**⚠️ 注意空格**:Application Password 生成后是 xxxx xxxx xxxx 带空格的 Base64 格式,直接复制粘贴进 Postman Basic Auth 的 Password 字段,**不要手动加引号或去掉空格**。我第一次用的时候以为是密码格式问题,把空格去掉了,结果连续 403 折腾了半小时。

---

💣 踩坑案例 3:跨域 CORS 预检请求直接 405

报错日志(浏览器 Network 面板):

Request URL: https://api.yoursite.com/wp-json/wp/v2/posts
Request Method: OPTIONS
Status: 405 Method Not Allowed

Response Headers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
# 没有 Allow-Headers: Content-Type, X-WP-Nonce

原因

跨域请求前,浏览器会先发 OPTIONS 预检(Preflight)。WordPress 默认不处理 OPTIONS,导致 405。这个问题在 Headless Next.js/React 项目里最常见。我的实际场景是:用 Next.js 做前端、WordPress 做后端,前端 fetch 一直 405,以为是 Nginx 配置问题,查了半天发现是 OPTIONS 根本没到 WordPress 就被 Nginx 返回 405 了。

解决方案

方案 A:Nginx 配置(推荐,已验证)

# 在你的 site nginx config 的 server {} 块内添加
location /wp-json/ {
    # 关键:处理 OPTIONS 预检
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,X-WP-Nonce';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        add_header 'Content-Length' 0;
        return 204;
    }

    add_header 'Access-Control-Allow-Origin' '*' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,X-WP-Nonce' always;
    add_header 'Access-Control-Expose-Headers' 'X-WP-Total, X-WP-TotalPages' always;
}
# 重载 Nginx
sudo nginx -t && sudo systemctl reload nginx

方案 B:WordPress 插件解决(如果无法改 Nginx)

安装 **"WP REST CORS"** 或 **"Access-Control-Allow-Origin"** 插件,在插件设置页面填 * 或你的前端域名。

验证:重启 Nginx 后,用浏览器 Network 面板重发请求,OPTIONS 应该返回 204 而不再是 405。

---

💣 踩坑案例 4:权限不足 — `rest_forbidden` 而非 401

报错日志

{
  "code": "rest_forbidden",
  "message": "Sorry, you are not allowed to do that.",
  "data": { "status": 403 }
}

原因

REST API 返回 403 说明认证通过,但用户没有对应权限。这在自定义 post type 或自定义 endpoint 上最常出现——注册时忘了加 show_in_rest => truepermission_callback。我遇到最典型的场景是:自定义 post type "book" 的 REST endpoint 报了 403,但普通文章 POST 就正常,原因是注册 post type 时 show_in_rest 默认为 false。

解决方案

排查 Step 1:检查用户角色权限

# 用 WP-CLI 查看用户角色
wp user get your_username --field=roles

# 如果是 Subscriber,默认无法发文章
# 提升为 Editor
wp user update your_username --role=editor

排查 Step 2:检查 post type 是否在 REST API 中可见

// 在主题 functions.php 或插件中
add_action('init', function () {
    // 重新注册 post type,确保 show_in_rest = true
    register_post_type('book', [
        'labels'       => ['name' => 'Books'],
        'public'       => true,
        'show_in_rest' => true,          // 关键!必须为 true
        'rest_base'    => 'books',
        'capability_type' => 'post',
    ]);
});

// 刷新 permalink
wp rewrite flush

排查 Step 3:检查 custom endpoint 的 permission_callback

add_action('rest_api_init', function () {
    register_rest_route('myplugin/v1', '/data', [
        'methods'  => 'POST',
        'callback' => function ($request) {
            return ['status' => 'ok'];
        },
        'permission_callback' => function () {
            // 这个函数必须返回 true,否则 403
            return current_user_can('edit_posts');  // 检查权限
        }
    ]);
});

---

💣 踩坑案例 5:Basic Auth 插件密码含特殊字符 URL 编码问题

报错日志

# 直接贴密码有特殊字符时报错
curl -u "username:P@ssw0rd!#$%" https://yoursite.com/wp-json/wp/v2/posts
# 可能变成 401 或 curl: (3) URL using bad/illegal format

原因

Shell 的 -u 参数会把整个字符串当作 username:password,如果密码含 #$% 等特殊字符,需要 URL 编码。实际中 Application Password 虽然不含特殊字符,但项目里如果自己实现 Basic Auth,密码里很容易有 #$ 导致请求直接失败。

解决方案

方案 A:PHP 脚本调用(避免 Shell 转义问题)

 [
        'method'  => 'GET',
        'header' => "Authorization: Basic " . base64_encode("$username:$password")
    ]
]);

$response = file_get_contents(
    'https://yoursite.com/wp-json/wp/v2/posts?per_page=5',
    false,
    $context
);

$data = json_decode($response, true);
echo "共 " . count($data) . " 篇文章\n";

**方案 B:cURL 加 --data-urlencode**

# 用 printf 避免 Shell 特殊字符问题
ENCODED=$(printf '%s' "username:xxxx xxxx xxxx" | base64)
curl -H "Authorization: Basic $ENCODED" \
     https://yoursite.com/wp-json/wp/v2/users/me

---

🛡️ 安全加固:REST API 暴露了哪些数据?

调试通之后,别忘了把不需要的 endpoint 关掉,减少攻击面:

// 在 functions.php 添加
// 禁止未登录用户访问公共 endpoint
add_filter('rest_endpoints', function ($endpoints) {
    // 移除某些敏感 endpoint(根据实际需求)
    if (!is_user_logged_in()) {
        unset($endpoints['/wp/v2/users']);
        unset($endpoints['/wp/v2/settings']);
        unset($endpoints['/wp/v2/themes']);
    }
    return $endpoints;
});

// 限制 pingback/xmlrpc(已有更好的替代方案)
add_filter('xmlrpc_enabled', '__return_false');
add_filter('pre_update_xmlrpc_disabled', '__return_true');

---

⚠️ 联盟声明

本文部分工具链接为 Amazon 联盟链接,我会从符合条件的购买中获得少量佣金,但这不影响我对工具本身的客观评测——我用的是 Postman 做 API 测试,单纯觉得它比 curl 直观才推荐。

✅ 调试工具箱(实测推荐)

工具用途
**Postman / Insomnia**手动测试 REST API 参数和认证
**WP-CLI**`wp rest route list` 查看所有可用端点
**Chrome DevTools → Network**看请求头/响应头具体哪里出问题
**Query Monitor 插件**查看 REST API 慢查询和权限判断
**WP Logger 插件**记录 REST 请求日志到文件

---

总结

WordPress REST API 认证失败的 5 个坑,总结下来就三句话:

1. **写操作必须带 Nonce** — Cookie 认证用 wp_create_nonce('wp_rest') 注入

2. **Application Passwords 带空格** — 直接复制生成的,格式是 xxxx xxxx xxxx

3. 跨域必配 CORS Nginx — OPTIONS 预检返回 204,不是 405

按这个清单查,基本能覆盖 90% 的 WordPress REST API 认证问题。如果你的报错不在上面,欢迎留言描述具体错误信息和环境配置。

---

延伸阅读

👉 Join MiniMax Token Plan: AI coding acceleration for businesses

👉 Join Zhipu Coding Plan: GLM-4.6/GLM-5 coding packages, China-stable, pay-per-token unlimited

👉 Join Aliyun AI: Top AI products with exclusive coupons for business innovation

📌 This article was AI-assisted generated and human-reviewed | TechPassive — An AI-driven content testing site focused on real tool reviews

🔗 Recommended Tools

These are carefully selected tools. Using our affiliate links supports us to keep producing quality content:

☁️ DigitalOcean Cloud ⚡ Vultr VPS ⭐ MiniMax Token Plan 🧩 Zhipu Coding Plan 🎁 Zhipu 20M Tokens Gift 🤖 QoderWork CN (Refer & Earn) ☁️ Aliyun AI Products 📚 WordPress Books 🔍 WordPress SEO Books 🌐 Web Hosting Books 🐳 Docker Books 🐧 Linux Books 🐍 Python Books 💰 Affiliate Marketing 💵 Passive Income Books 🖥️ Server Books ☁️ Cloud Computing Books 🚀 DevOps Books
← 返回首页