ロリポップ+Cloudflareで「更新に失敗しました。サーバーから正しい応答がありませんでした」が出たときの解決策

WordPressで投稿しようとしたら 「更新に失敗しました。サーバーから正しい応答がありませんでした。」 というエラー。管理画面には入れるのに、投稿だけが保存できない——。

調べると同じ症状の記事はたくさん出てきますが、定番の解決策をすべて試しても直りませんでした。原因はかなり特殊で、標準的に使われるヘッダ(X-Forwarded-Proto)が壊れた値で届いていたことにありました。

同じ環境(ロリポップ+Cloudflare+外部ドメイン)でハマっている人向けに、経緯・原因・解決策をまとめます。


環境

  • サーバー:ロリポップ
  • ドメイン:Cloudflareで取得・管理している外部ドメイン
  • 構成:Cloudflareのサブドメインを、CNAMEでロリポップに向けて利用
  • SSL:Cloudflareのプロキシ(オレンジ雲)でHTTPS化

ロリポップの無料SSLは「ロリポップ側で管理しているドメイン」しか使えないため、外部ドメインではCloudflareのHTTPSに頼る構成になっていました。


症状

  • WordPressの管理画面には普通にアクセスできる
  • しかし投稿しようとすると 「更新に失敗しました。サーバーから正しい応答がありませんでした。」
  • Cloudflareのプロキシをオフ(HTTP接続)にすると、投稿できるようになる
  • wp-adminをHTTPS化しようとすると、今度は 「リダイレクトが繰り返し行われました」 でループ

つまり「HTTPSにすると壊れる/HTTPなら動く」という状態でした。


原因

ポイントは、間にCloudflareがいると通信のプロトコルがすり替わることです。

ブラウザ ──HTTPS──> Cloudflare ──HTTP──> ロリポップ(WordPress)

ブラウザとCloudflareの間はHTTPSですが、Cloudflareとロリポップの間はHTTP(平文)。そのため、ロリポップに届く時点では「HTTPのリクエスト」になっています。

WordPressは届いたものしか見られないので、「HTTPで来た」と誤認します。一方でサイトURLはHTTPS。この食い違いが、

  • 投稿時:REST APIの通信がプロトコル不整合で失敗 →「更新に失敗しました。サーバーから正しい応答がありませんでした」
  • 管理画面:HTTPS化しようとすると「HTTPと誤認 → HTTPSへリダイレクト」を繰り返す → 無限ループ

を引き起こしていました。

解決を阻んだ「特殊な落とし穴」

この問題の定番解決策は、wp-config.phpに以下を入れて「元はHTTPSだよ」とWordPressに教えることです。

if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}

X-Forwarded-Proto は、中継サーバーが「元のプロトコルはこれ」と伝えるための業界標準のヘッダです。ほとんどの環境ではこれで解決します。

ところが、この環境では効きませんでした。

実際にサーバーへ届いているヘッダを確認用PHPで調べたところ、次のようになっていました。

HTTP_X_FORWARDED_PROTO = http          ← https のはずが http で届いている!
HTTP_CF_VISITOR = {"scheme":"https"}   ← こちらは正しく https

標準であるはずの X-Forwarded-Proto が、なぜか http という誤った値で届いていたのです。だから、このヘッダを見るコードは永遠に成立せず、ループが直りませんでした。

一方、Cloudflare独自のヘッダ CF-Visitor には、正しく https が入っていました。


届いているヘッダを実際に確認する方法

推測で悩むより、何が届いているか実測するのが確実です。WordPressのフォルダに、推測されにくい名前で確認用PHPを置きます(例:check-headers-9821.php)。

<?php
header('Content-Type: text/plain');
foreach ($_SERVER as $key => $value) {
    if (stripos($key, 'PROTO') !== false
        || stripos($key, 'HTTPS') !== false
        || stripos($key, 'FORWARD') !== false
        || stripos($key, 'CF') !== false
        || stripos($key, 'SCHEME') !== false) {
        echo $key . ' = ' . $value . "\n";
    }
}

Cloudflareのプロキシをオンにした状態で https://あなたのドメイン/check-headers-9821.php にアクセスし、出力を確認します。

  • X-Forwarded-Proto = https が出ていれば、標準ヘッダで解決可能
  • CF-Visitor = {"scheme":"https"} が出ていれば、そちらを使えば解決

確認が終わったら、このファイルは必ず削除してください。 サーバー情報が見える状態は危険です。


解決策

1. wp-config.phpを修正(CF-Visitorを見る)

require_once ABSPATH . 'wp-settings.php';行より上に、以下を追記します。

// Cloudflare経由のHTTPSを正しく認識させる
if (isset($_SERVER['HTTP_CF_VISITOR'])) {
    $cf_visitor = json_decode($_SERVER['HTTP_CF_VISITOR'], true);
    if (isset($cf_visitor['scheme']) && $cf_visitor['scheme'] === 'https') {
        $_SERVER['HTTPS'] = 'on';
    }
}

define('WP_HOME', 'https://あなたのドメイン');
define('WP_SITEURL', 'https://あなたのドメイン');

define('FORCE_SSL_ADMIN', true);

これでWordPressが自分をHTTPSと正しく認識し、ループも投稿エラーも解消します。

ポイント$_SERVER['HTTPS'] = 'on' のセットを先に実行し、FORCE_SSL_ADMINに置くこと。順番が逆だとループが直りません。

2. CloudflareのSSL/TLS設定

オリジン(ロリポップ)に証明書がないため、SSL/TLSモードは構成に合わせて設定します。

  • 理想形:ゾーン全体は Full (Strict)、対象サブドメインだけ Configuration RulesFlexible に上書き

こうすると、平文HTTPになるのは証明書のない対象サブドメインのオリジン接続だけに限定でき、他のサイトやCloudflare Tunnelなどは本来の暗号化を保てます。

Configuration Rulesの設定例

  • 場所:Cloudflareダッシュボード → RulesConfiguration RulesCreate rule
  • 条件:Hostname equals 対象のサブドメイン
  • 設定:SSLFlexible に指定


切り分けの手順(同じ症状の人向け)

闇雲に設定をいじると混乱します。次の順で一つずつ確認するのがおすすめです。

  1. wp-config.phpの追記を全部外し、Cloudflareをプロキシなし(グレー雲)に戻す
    → この素の状態で投稿できれば、WordPress本体とサーバーは正常。問題はCloudflare経由時だけと確定。
  2. CloudflareのSSL/TLSをFlexibleにする
  3. プロキシをオン(オレンジ雲)に戻す
  4. それでも投稿できない/ループするなら、確認用PHPで届いているヘッダを実測
  5. 結果に応じて、X-Forwarded-ProtoCF-Visitor のどちらを見るか決める

まとめ

  • 症状:管理画面には入れるのに投稿で「更新に失敗しました。サーバーから正しい応答がありませんでした」、HTTPS化するとリダイレクトループ
  • 原因:Cloudflare経由でプロトコルがHTTPに変わり、WordPressがHTTPと誤認。さらに標準のX-Forwarded-Protohttpという誤った値で届くという特殊事情があった
  • 解決X-Forwarded-Protoではなく、Cloudflare独自のCF-Visitorを見てHTTPSを認識させる

定番の解決策(X-Forwarded-Proto)で直らないときは、推測せず、実際に届いているヘッダを確認すること。これが遠回りに見えて一番の近道でした。

タイトルとURLをコピーしました