「更新に失敗しました。返答が正しいJSONレスポンスではありません」の対処法

実測 に検証

記事を保存しようとしたら、このメッセージが出た。

更新に失敗しました。返答が正しい JSON レスポンスではありません。

更新に失敗しました。返答が正しい JSON レスポンスではありません。

.htaccess で wp-json を塞いだ状態で、ブロックエディターから保存した画面

ブロックエディタが REST API を呼んだのに、JSON ではないものが返ってきた という意味です。何が返っているのかを実際に見れば原因は分かります。

まず何が返っているか見る

ブラウザの開発者ツールでもいいですが、コマンドで叩くのが速いです。

curl -s -o /dev/null -D - "https://example.com/wp-json/"

見るのは 2 つだけです。

見るところ正常
ステータス200
Content-Typeapplication/json; charset=UTF-8

text/html が返っていたら、それが原因です。

原因 1: REST API が塞がれている

セキュリティ目的で /wp-json を遮断する設定が入っていることがあります。 .htaccess に次のような規則が書かれているケースです。

RewriteRule ^wp-json/ - [F,L]

実測すると、こうなりました。

ステータスContent-Type本文
Apache403text/htmlApache の 403 Forbidden ページ
nginx200application/json正常

HTML が返るので、エディタは「正しい JSON ではない」と言います。 メッセージは JSON の話をしていますが、原因は REST API に到達できないことです。

nginx 側が影響を受けていないのは、.htaccess を読むのが Apache だけだからです。 レンタルサーバーはほぼ Apache なので、この経路は実務で頻出します。

.htaccess を見るときの注意

書いてある位置によって、効いているかどうかが変わります。

WordPress が生成するブロックは RewriteRule . /index.php [L] で終わります。 [L] で処理が止まるため、このブロックより後に書かれたリライト規則は 実行されません。

実測でも、# BEGIN WordPress より後に置いた遮断ルールはまったく効かず、 前に移した瞬間に 403 になりました。

つまり .htaccess に怪しい記述があっても、WordPress ブロックより後ろなら それは原因ではありません。探すのは前半です。

同じ遮断は、セキュリティプラグインや WAF(ModSecurity)でも起きます。 .htaccess に何もなければ、そちらを疑います。

原因 2: REST API の中で Fatal error が起きている

プラグインが REST の処理中に Fatal を起こすと、JSON の代わりに PHP のエラーが返ります。厄介なのはステータスが 200 のままなことです。

実測値です。

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8

<br />
<b>Fatal error</b>: Uncaught Error: Call to undefined function ...

ステータスは 200、Content-Type は JSON、中身は HTML。 REST サーバーはコールバックを呼ぶ前にヘッダを送り終えているため、 あとから 500 に変えられません。

死活監視は 200 を受け取るので気づきません。 気づくのは「保存できない」という報告が来たときです。

詳しくは サイトが壊れているのに監視は正常(HTTP 200)

原因 3: 認証が通っていない

ログインしているのに 401 が返ることがあります。

{"code":"rest_not_logged_in","message":"現在ログインしていません。","data":{"status":401}}

REST API の Cookie 認証は X-WP-Nonce ヘッダが必須です。 無いと未ログイン扱いになります。

これは仕様なので、curl で叩いて 401 が返るのは正常です。 エディタからのリクエストには nonce が付くので 200 になります(実測で確認)。

「ブラウザでは動くのに curl では 401」の正体はこれで、 この場合は原因ではありません。混同しないようにします。

ただしログインしたまま長時間エディタを開いていた場合は別で、 nonce の有効期限切れで実際に失敗します。画面を再読み込みすれば直ります。

原因 4: レスポンスの前に余計な出力が混ざっている

テーマの functions.php の末尾に空白があると、JSON の前にその空白や Warning が出力されて、パースに失敗します。

この場合は REST だけでなくログインもできなくなるので、症状で区別できます。 /wp-login.phpSet-Cookie が 0 個なら、こちらです。

curl -s -o /dev/null -D - "https://example.com/wp-login.php" | grep -ci set-cookie

詳しくは functions.php を壊したときの復旧

切り分けの順番

# 1. REST のトップが JSON を返すか
curl -s -o /dev/null -D - "https://example.com/wp-json/" | grep -iE '^HTTP|^content-type'
結果原因
403 + text/html遮断されている(原因 1)
200 + application/json なのに本文が HTMLREST 内で Fatal(原因 2)
401 rest_not_logged_in正常(原因ではない)
200 + JSON だが先頭に空白や Warning余計な出力(原因 4)
404パーマリンク設定かサーバー設定。設定を保存し直す
200 + 正常な JSONREST は無罪。JS 側(プラグイン競合)を疑う

最後の「REST は正常なのにエディタが失敗する」場合は、 ブラウザのコンソールに JS のエラーが出ていないか見ます。 jQuery の二重読み込みなどプラグイン競合の可能性があります。

詳しくは プラグインの競合

応急処置として使えるもの

クラシックエディタに切り替えると、REST API を使わずに保存できます。 原因を直すまでの時間を稼げます。ただし REST API はエディタ以外 (スマホアプリ、外部連携、一部プラグイン)も使うので、 遮断を放置すると別の場所で問題が出ます。