結論
wp_remote_get() の失敗は、次の3層に分けて確認します。層を飛ばして json_decode を書くと、原因が分からなくなります。
- 通信自体の失敗: 戻り値が
WP_Error。is_wp_error()で判定し、get_error_message()を読む。 - 通信は成功したが、HTTPステータスが 200 系でない:
wp_remote_retrieve_response_code()で判定。 - 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 が失敗する原因と対処の関連項目
- wp_remote_get() の使い方|説明・引数・注意点
- wp_safe_remote_get() の使い方|説明・引数・注意点
- wp_remote_retrieve_response_code() の使い方|説明・引数・注意点
- wp_remote_retrieve_body() の使い方|説明・引数・注意点
- is_wp_error() の使い方|説明・引数・注意点
