2026年6月19日
2026年6月19日
WordPressのREST APIで「400 Bad Request」が返る原因と対処
WordPressのREST APIが「400 Bad Request」を返すのは、ほとんどの場合、送ったリクエストの中身(パラメーターの型や値、JSONの形式)をWordPressが受け付けなかったためです。400の応答には、理由を示す code と message がJSONで入っているので、まずそれを読めば原因はかなり絞り込めます。この記事では、応答の確かめ方、エラーコードごとの原因と直し方、WordPressより手前のサーバーが400を返している場合の見分け方を説明します。
まず応答の本文を読む
400は「リクエストに問題がある」という大まかな意味しか持たないため、ステータスコードだけを見ても原因は分かりません。WordPressのREST APIは、エラーのときに次のようなJSONを返します。
{
"code": "rest_invalid_param",
"message": "Invalid parameter(s): per_page",
"data": {
"status": 400,
"params": {
"per_page": "per_page must be between 1 (inclusive) and 100 (inclusive)"
}
}
}
code が原因の種類、data.params がどのパラメーターがなぜだめだったかを表します。メッセージはサイトの言語設定によっては日本語で返り、文言はバージョンによって少し変わりますが、code の値は変わりません。調べるときは code を手がかりにしてください。
応答の確かめ方は2通りあります。
- ブラウザから送っている場合: 開発者ツールの「ネットワーク」タブで該当のリクエストを選び、「レスポンス」(Response)を開く
- サーバーやPCから送っている場合:
curlに-iを付けて、ヘッダーと本文をまとめて表示する
curl -i "https://example.com/wp-json/wp/v2/posts?per_page=200"
JavaScriptの fetch を使っているなら、response.ok が false のときに await response.json() で本文を読み、コンソールに出すようにしておくと、原因を見落としません。
本文がJSONではなく、HTMLのエラーページだった場合は、WordPressまで届く前にWebサーバーやWAFが400を返しています。この場合は後半の「WordPressより手前で400が返っている場合」に進んでください。
原因1:パラメーターの値が受け付けられない(rest_invalid_param)
もっとも多いのがこのコードです。エンドポイントごとに、受け付けるパラメーターの型や範囲が決められていて、それに合わない値を送ると400になります。よくある例を挙げます。
per_pageに100より大きい値を指定した(コアの一覧系エンドポイントは1〜100まで)statusにpublish、draftなどの決められた値以外を入れたcategoriesやtagsに、IDではなく名前やスラッグを入れたdateに、2026-10-17T09:00:00のような形式ではない文字列を入れた- 数値を期待する項目に空文字を送った
対処は、data.params に書かれたパラメーターを、エンドポイントの仕様に合わせて直すことです。各エンドポイントが何を受け付けるかは、そのURLに OPTIONS メソッドでリクエストすると、args として一覧が返ります。
curl -X OPTIONS -i "https://example.com/wp-json/wp/v2/posts"
100件より多くの投稿を取得したい場合は、per_page=100 にして page=2、page=3 と順に取得します。応答ヘッダーの X-WP-Total(総件数)と X-WP-TotalPages(総ページ数)を見れば、何ページまであるか分かります。
原因2:必須のパラメーターが無い(rest_missing_callback_param)
エンドポイントが必須としているパラメーターが届いていないと、このコードになります。「送ったはずなのに無いと言われる」場合は、値の名前の書き間違いのほかに、次の原因3を疑ってください。
原因3:JSONの送り方が正しくない(rest_invalid_json など)
本文をJSONで送るときは、Content-Type: application/json ヘッダーが必要です。このヘッダーが無いと、WordPressは本文をJSONとして読まないため、中身が空として扱われ、原因2の「必須のパラメーターが無い」や、何も更新されないといった結果になります。ヘッダーは付いているのに、JSONの書式そのものが壊れている(末尾のカンマ、引用符の閉じ忘れ、文字列を二重にJSON化している)場合は、rest_invalid_json が返ります。
fetch で送るときは、オブジェクトを JSON.stringify で文字列にし、ヘッダーを付けます。
const res = await fetch( '/wp-json/wp/v2/posts/123', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': wpApiSettings.nonce, // ログイン中の利用者として送る場合
},
body: JSON.stringify( { title: '新しいタイトル' } ),
} );
if ( ! res.ok ) {
console.error( await res.json() );
}
ここで使っている wpApiSettings は、PHP側で wp_enqueue_script の依存に wp-api-request を指定したときにWordPressが出力する変数です。自分で wp_create_nonce( 'wp_rest' ) の値を wp_localize_script などで渡す方法でもかまいません。エディターや管理画面の中のスクリプトなら、@wordpress/api-fetch の apiFetch( { path, method, data } ) を使うと、ヘッダーとnonceを自動で付けてくれるので、この種の間違いが起きにくくなります。
なお、nonceが無い・古い場合は、400ではなく401や403が返るのが通常です。ステータスが違う場合は、認証の問題として切り分けてください。
原因4:存在しないページを指定した(rest_post_invalid_page_number)
一覧を取得するときに、総ページ数より大きい page を指定すると、400が返ります。無限スクロールや、ページを順に取得する処理で、終わりの判定をしていないと起こります。X-WP-TotalPages ヘッダーの値を見て、それを超えたら取得をやめるようにします。
原因5:自作エンドポイントの検証で落ちている
register_rest_route() で作ったエンドポイントでは、args に書いた validate_callback が false を返すか、type や required の条件に合わないと、WordPressが自動で400を返します。コールバックの中で、自分で WP_Error を返している場合も同様です。
<?php
add_action( 'rest_api_init', function () {
register_rest_route( 'myplugin/v1', '/score', array(
'methods' => 'POST',
'callback' => 'myplugin_save_score',
'permission_callback' => function () {
return current_user_can( 'edit_posts' );
},
'args' => array(
'score' => array(
'type' => 'integer',
'required' => true,
'minimum' => 0,
'maximum' => 100,
),
),
) );
} );
function myplugin_save_score( WP_REST_Request $request ) {
$score = (int) $request->get_param( 'score' );
// 保存処理(省略)
return rest_ensure_response( array( 'saved' => $score ) );
}
この例では、score が無ければ rest_missing_callback_param、整数でなかったり0〜100の範囲を外れたりすれば rest_invalid_param が返ります。思わぬ値で落ちている場合は、送っている値と args の条件を突き合わせます。数字を文字列 "50" として送っても、type が integer なら変換して受け付けられますが、"50点" のような文字列は受け付けられません。
どのリクエストが400になっているかをサーバー側で記録したい場合は、rest_post_dispatch フィルターでエラーの応答だけをログに出す方法があります。調査用のコードなので、終わったら外してください。
<?php
add_filter( 'rest_post_dispatch', function ( $result, $server, $request ) {
if ( $result instanceof WP_REST_Response && 400 === $result->get_status() ) {
error_log( sprintf(
'REST 400: %s %s %s',
$request->get_method(),
$request->get_route(),
wp_json_encode( $result->get_data() )
) );
}
return $result;
}, 10, 3 );
WP_DEBUG_LOG を有効にしていれば、wp-content/debug.log に記録されます。リクエストの本文には個人情報が含まれることがあるため、ここでは本文ではなく応答のエラー内容だけを記録しています。
WordPressより手前で400が返っている場合
応答がJSONではなくHTMLだった場合や、/wp-json/ 以外のページでも同じ400が出る場合は、Webサーバー、CDN、WAFのどれかが返しています。代表的なものは次のとおりです。
- 「Request Header Or Cookie Too Large」: Nginxが、Cookieやヘッダーが大きすぎると判断している。ブラウザのCookieを削除して改善するか確認し、改善するなら、Cookieを大量に書き込んでいるプラグインがないか調べる
- 「The plain HTTP request was sent to HTTPS port」: HTTPS用のポートにHTTPで接続している。リクエストのURLやリバースプロキシの設定を見直す
- サーバー会社やCDN独自のエラーページ: WAFがリクエストの中身を攻撃と判断した可能性がある。サーバー会社のコントロールパネルでWAFのログを確認し、誤検知であれば除外設定を検討する
WAFは403を返すことが多いものの、製品や設定によっては400を返すこともあります。サーバーのアクセスログで、該当のリクエストがWordPress(PHP)まで届いているかを確認すると、どの層で止まっているかを判断できます。サーバーやWAFの設定を変える前には、元の設定を控えておいてください。
調査の順番
- 応答の本文を読み、JSONかHTMLかを確認する
- JSONなら
codeとdata.paramsを見て、該当する原因の節に進む OPTIONSリクエストで、エンドポイントが受け付けるパラメーターを確認する- JSONで送っている場合は
Content-Typeヘッダーと本文の書式を確認する - HTMLなら、サーバーやWAFのログで止まっている層を特定する
まとめ
- REST APIの400は、送ったリクエストの中身が受け付けられなかったことを表し、理由は応答のJSONの
codeとmessageに書かれている - 多いのは
rest_invalid_param(値の型や範囲)、rest_missing_callback_param(必須項目の不足)、rest_invalid_json(JSONの書式) - JSONで送るときは
Content-Type: application/jsonが必要で、無いと中身が届かない per_pageは100まで、pageは総ページ数まで。応答ヘッダーで件数を確認する- 応答がHTMLなら、WordPressより手前のサーバーやWAFが原因
エラーコード名や制限値は、WordPressの仕様に基づいています(2026年10月現在)。