WordPress REST API 认证调试踩坑复盘
做 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 => true 或 permission_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: