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の設定を変える前には、元の設定を控えておいてください。

調査の順番

  1. 応答の本文を読み、JSONかHTMLかを確認する
  2. JSONなら code と data.params を見て、該当する原因の節に進む
  3. OPTIONS リクエストで、エンドポイントが受け付けるパラメーターを確認する
  4. JSONで送っている場合は Content-Type ヘッダーと本文の書式を確認する
  5. 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月現在)。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲