wp_remote_get が失敗する原因と対処|WP_Error の見方と安全な書き方

結論

wp_remote_get() の失敗は、次の3層に分けて確認します。層を飛ばして json_decode を書くと、原因が分からなくなります。

  1. 通信自体の失敗: 戻り値が WP_Error。is_wp_error() で判定し、get_error_message() を読む。
  2. 通信は成功したが、HTTPステータスが 200 系でない: wp_remote_retrieve_response_code() で判定。
  3. 200 だが、本文が期待した形式ではない: json_decode の結果と json_last_error_msg() を確認。

公式リファレンスでも、戻り値は「レスポンスの配列、または WP_Error」と説明されています。成功時の配列を前提に $res['body'] を読むと、失敗時に警告や致命的エラーになります。

原因・仕組み

wp_remote_get() は内部で WordPress の HTTP API を使い、cURL や streams のどちらかで通信します。公式リファレンスの既定値は、timeout が 5 秒、redirection が 5 回、sslverify が true、user-agent が WordPress/バージョン; サイトURL です。

よくある失敗原因は、次のように分類できます。

層 例 代表的な原因
通信 cURL error 7 接続拒否。相手が停止、ファイアウォール、ポート違い
通信 cURL error 6 名前解決の失敗。ホスト名の誤り、DNS 不通
通信 cURL error 28 タイムアウト。相手が遅い、timeout が短い
通信 cURL error 60 SSL 証明書の検証失敗。古い CA バンドル、自己署名証明書
ステータス 403 / 429 / 5xx User-Agent 拒否、レート制限、相手側の障害
本文 JSON 不正 HTML のエラーページが返る、BOM 付き、文字コード違い

手順(サンプルコード)

3層を順に確認し、失敗理由を WP_Error にまとめて返す関数です。

function ks_fetch_json( $url ) {
    $res = wp_remote_get( $url, array( 'timeout' => 3 ) );
    if ( is_wp_error( $res ) ) {
        return new WP_Error( 'ks_http', $res->get_error_code() . ': ' . $res->get_error_message() );
    }
    $code = wp_remote_retrieve_response_code( $res );
    if ( 200 !== $code ) {
        return new WP_Error( 'ks_status', "HTTP $code" );
    }
    $data = json_decode( wp_remote_retrieve_body( $res ), true );
    if ( null === $data ) {
        return new WP_Error( 'ks_json', 'JSON を解釈できません: ' . json_last_error_msg() );
    }
    return $data;
}

ユーザーが入力した URL を取得する場合は、wp_safe_remote_get() に置き換えます。内部ネットワーク宛てなどの危険な URL を弾く目的です。

動作確認(検証環境と結果)

WordPress 7.1.2、PHP 8.2.12 で、上の関数を実行しました(php C:Tempwp-testrun.php wp-remote-get-failed-wp-error.php)。

  • 待受のないポート http://127.0.0.1:9/: http_request_failed: cURL error 7: Failed to connect to 127.0.0.1 port 9。
  • 存在しないホスト http://nonexistent.invalid/: cURL error 6: Could not resolve host。
  • wp_safe_remote_get( 'http://127.0.0.1/' ): 「有効な URL ではありません。」の WP_Error(内部アドレスが拒否された)。
  • pre_http_request フィルターで疑似レスポンスを返した場合: 404 は HTTP 404、HTML 本文は JSON を解釈できません: Syntax error、正しい JSON は配列として取得できた。
  • http_request_args の既定値: timeout=5 redirection=5 sslverify=true、User-Agent は WordPress/7.1.2; http://localhost。

インターネット越しの実在 API への接続、cURL error 28 や 60 の再現は行っていません。この2つは表の一般的な原因として記載したもので、本環境では未検証です。

wp_remote_get が失敗する原因と対処の注意点

  • sslverify => false は、証明書エラーの根本解決ではありません。中間者攻撃のリスクが残るため、本番では使わず、サーバーの CA バンドルを更新します。
  • 外部 API の結果は、set_transient() で数分〜数時間キャッシュします。ページ表示のたびに通信すると、遅い・止まるの原因になります(set_transient() の使い方|説明・引数・注意点)。
  • API キーは、コードに直書きせず、定数や環境変数に置きます。ログにも出さないでください。
  • 失敗時のログは、URL のクエリにキーが含まれていないか確認してから書き出します。
  • レスポンスヘッダーが必要な場合は wp_remote_retrieve_headers() の使い方|説明・引数・注意点 を使います。

wp_remote_get が失敗する原因と対処でよくあるミス

  • is_wp_error() を確認せずに、戻り値を配列として扱う。
  • ステータスを見ずに json_decode し、404 の HTML を JSON として解釈しようとする。
  • 管理画面やフロントの表示処理の中で、同期的に遅い API を毎回呼ぶ。
  • POST に wp_remote_get() を使う(POST は wp_remote_post()、wp_remote_post() の使い方|説明・引数・注意点)。
  • ローカル開発環境で自分自身のサイトを呼び、リバースプロキシやBasic認証で失敗する。

wp_remote_get が失敗する原因と対処のチェックリスト

  • 戻り値を is_wp_error() で確認したか。
  • ステータスコードを確認したか。
  • JSON のデコード失敗を処理したか。
  • timeout を用途に合わせたか。
  • 結果をキャッシュしたか。
  • ユーザー入力の URL には wp_safe_remote_get() を使ったか。

wp_remote_get が失敗する原因と対処のFAQ(よくある質問)

Q. タイムアウトを延ばせば解決しますか。
A. 相手が遅いだけなら有効ですが、延ばすほど表示も遅くなります。キャッシュとバックグラウンド取得(wp_schedule_event() の使い方|説明・引数・注意点)が基本です。

Q. cURL error 60 が出ます。
A. 一般に、サーバーの CA 証明書が古い、または自己署名証明書です。サーバー側の更新を優先し、検証無効化は避けます。

Q. wp_remote_get と file_get_contents の違いは?
A. WordPress の HTTP API は、環境ごとの通信方式の違いを吸収し、フィルターで制御もできます。WordPress 内では前者を使うのが一般的です。

筆者の見解(wp_remote_get が失敗する原因と対処)

外部通信は、必ず失敗する前提で書くべきだと考えます。成功のコードを先に書き、失敗の分岐を後から足すと、3層のどれかが抜けやすくなります。上の関数のように、失敗の理由を呼び出し側へ返す形にしておくと、画面への表示かログかを、用途に合わせて選べます。

wp_remote_get が失敗する原因と対処の関連項目

出典(一次情報)

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