2026年6月17日
2026年6月17日
WordPressのREST APIエラーを解決する方法
はじめに
WordPressのREST APIが403 Forbidden・401 Unauthorized・404 Not Foundになる問題は、パーマリンク設定・認証トークンの不備・プラグインによるブロックが原因であることが多いです。
症状・原因
/wp-json/wp/v2/postsにアクセスすると403または404になる- JavaScript から fetch した REST API が認証エラーになる
- カスタムエンドポイントに登録したのに404になる
- REST APIを無効化するプラグインが競合している
解決手順
ステップ1:REST APIの状態を確認する
# REST APIのルートインデックスを確認
curl -s https://example.com/wp-json/ | jq '.namespaces'
# wp/v2エンドポイントを確認
curl -s https://example.com/wp-json/wp/v2/posts?per_page=1 | jq '.[0].id'
# 認証なしでアクセスできるか確認
curl -I https://example.com/wp-json/wp/v2/users
# WP-CLIでREST APIをテスト
wp eval "
\$response = rest_do_request(new WP_REST_Request('GET', '/wp/v2/posts'));
echo 'Status: ' . \$response->get_status() . PHP_EOL;
echo 'Count: ' . count(\$response->get_data()) . PHP_EOL;
"
ステップ2:パーマリンクと基本設定を修正する
# パーマリンクをリセット(REST APIのルートも再生成される)
wp rewrite flush --hard
# REST APIが有効かオプションを確認
wp option get permalink_structure
# 空の場合はパーマリンクが「基本」設定になっており REST API が動かない
# パーマリンクを投稿名に変更
wp option update permalink_structure '/%postname%/'
wp rewrite flush --hard
// functions.php: REST APIを誤って無効化しているコードを削除する
// 以下のようなコードがあれば削除
// add_filter('rest_authentication_errors', function() { return new WP_Error('disabled', '...'); });
// REST APIへのアクセスを特定ユーザーに制限する(正しい方法)
add_filter('rest_authentication_errors', function(mixed $result): mixed {
if (!is_user_logged_in() && !empty($result)) {
return $result;
}
// 公開エンドポイントはそのまま許可
return $result;
});
ステップ3:カスタムエンドポイントを正しく登録する
// functions.php: カスタム REST API エンドポイントを登録
add_action('rest_api_init', function(): void {
register_rest_route('my-plugin/v1', '/items', [
'methods' => WP_REST_Server::READABLE, // GET
'callback' => 'my_get_items_callback',
'permission_callback' => '__return_true', // 認証不要
'args' => [
'per_page' => [
'type' => 'integer',
'default' => 10,
'minimum' => 1,
'maximum' => 100,
'sanitize_callback' => 'absint',
],
],
]);
register_rest_route('my-plugin/v1', '/items/(?P<id>\d+)', [
[
'methods' => WP_REST_Server::READABLE,
'callback' => 'my_get_item_callback',
'permission_callback' => '__return_true',
'args' => [
'id' => ['validate_callback' => fn($v) => is_numeric($v)],
],
],
[
'methods' => WP_REST_Server::EDITABLE, // PUT/PATCH
'callback' => 'my_update_item_callback',
'permission_callback' => fn() => current_user_can('edit_posts'),
],
]);
});
function my_get_items_callback(WP_REST_Request $request): WP_REST_Response {
$per_page = $request->get_param('per_page') ?? 10;
$posts = get_posts(['posts_per_page' => $per_page, 'post_status' => 'publish']);
return new WP_REST_Response(array_map(function(WP_Post $post): array {
return [
'id' => $post->ID,
'title' => $post->post_title,
'url' => get_permalink($post->ID),
];
}, $posts), 200);
}
ステップ4:JavaScriptからの認証(nonce)を正しく設定する
// functions.php: フロントエンドのJavaScriptにnonceを渡す
add_action('wp_enqueue_scripts', function(): void {
wp_enqueue_script(
'my-app',
get_template_directory_uri() . '/js/app.js',
['wp-api-fetch'],
filemtime(get_template_directory() . '/js/app.js'),
true
);
wp_localize_script('my-app', 'myApp', [
'apiUrl' => esc_url_raw(rest_url()),
'nonce' => wp_create_nonce('wp_rest'),
]);
});
// js/app.js: WordPress REST API への認証リクエスト
// ① wp-api-fetch を使う(推奨)
wp.apiFetch.use(wp.apiFetch.createNonceMiddleware(myApp.nonce));
wp.apiFetch({ path: '/wp/v2/posts?per_page=5' })
.then(posts => console.log(posts))
.catch(err => console.error(err));
// ② fetch を直接使う場合
fetch(`${myApp.apiUrl}wp/v2/posts`, {
headers: {
'X-WP-Nonce': myApp.nonce,
'Content-Type': 'application/json',
},
})
.then(res => res.json())
.then(data => console.log(data));
ステップ5:CORSエラーと外部からのアクセスを許可する
// functions.php: REST APIにCORSヘッダーを追加
add_action('rest_api_init', function(): void {
remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
add_filter('rest_pre_serve_request', function(mixed $value): mixed {
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
$allowed = ['https://app.example.com', 'https://admin.example.com'];
if (in_array($origin, $allowed, true)) {
header('Access-Control-Allow-Origin: ' . $origin);
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Headers: Authorization, Content-Type, X-WP-Nonce');
}
return $value;
});
});
// Application Password を使った外部からの認証(WordPress 5.6以降)
// Authorization: Basic base64(username:application_password) ヘッダーを付与
# CORSエラーをcurlで確認
curl -I -H "Origin: https://app.example.com" \
https://example.com/wp-json/wp/v2/posts
# Application Password での認証テスト
curl -u "username:xxxx xxxx xxxx xxxx xxxx xxxx" \
https://example.com/wp-json/wp/v2/users/me
注意事項
permission_callbackに'__return_true'を設定すると認証なしでアクセスできます。機密データを返すエンドポイントでは必ずcurrent_user_can()等で権限チェックを行ってください- REST APIを完全に無効化するプラグイン(Disable REST API等)がインストールされていると、他のプラグインやGutenbergが動作しなくなります。無効化するより認証ベースのアクセス制限を推奨します
- Application Passwordはアプリケーションパスワード(スペース区切り24文字)をBasic認証で使用します。通常のログインパスワードとは別に管理画面の「ユーザー → プロフィール」から発行してください
まとめ
REST APIエラーの解決は①curlでエンドポイントにアクセスしてHTTPステータスを確認、②wp rewrite flush --hardでパーマリンクをリセットしREST APIルートを再生成、③register_rest_route()でrest_api_initフックに正しくエンドポイントを登録しpermission_callbackを設定、④wp_create_nonce('wp_rest')で生成したnonceをX-WP-Nonceヘッダーに付与してJS認証、⑤外部サービスからのアクセスはCORSヘッダー追加またはApplication Passwordで対応します。