2026年8月3日

2026年8月3日

WordPressのHTTP APIで外部APIを呼ぶ方法(wp_remote_get/postの引数とエラー処理)

WordPress のテーマやプラグインから外部の API を呼ぶときは、PHP の cURL 関数を直接使わずに、WordPress の HTTP API(wp_remote_get() や wp_remote_post() など)を使います。サーバーの環境差を吸収し、プロキシ設定やフィルターによる差し替えにも対応できるためです。この記事では、GET と POST の書き方、主な引数の既定値、エラー処理、キャッシュ、テスト時の差し替えまでを順に説明します。動作は WordPress 6.9 で確認しています(2026年10月時点)。

HTTP APIの関数一覧

関数 用途
wp_remote_get( $url, $args ) GET リクエスト
wp_remote_post( $url, $args ) POST リクエスト
wp_remote_head( $url, $args ) HEAD リクエスト(本文なしでステータスやヘッダーだけ確認)
wp_remote_request( $url, $args ) method 引数で PUT・PATCH・DELETE なども送れる汎用版
wp_safe_remote_get() など wp_safe_ 付きの関数 URL の検証を有効にした版(後述)

戻り値は、成功時は headers・body・response・cookies などを持つ配列、通信そのものに失敗したときは WP_Error です。配列から値を取り出すときは、次の関数を使います。

  • wp_remote_retrieve_response_code( $response ): ステータスコード(200、404 など)
  • wp_remote_retrieve_body( $response ): 本文(文字列)
  • wp_remote_retrieve_header( $response, 'content-type' ): 指定したヘッダーの値
  • wp_remote_retrieve_headers( $response ): ヘッダー全体

主な引数と既定値

第2引数の配列で、リクエストの動きを変えられます。既定値は WP_Http::request() で決まっています。

引数 既定値 意味
timeout 5(秒) 応答を待つ秒数
redirection 5 リダイレクトをたどる回数(HEAD は0)
httpversion '1.0' HTTP のバージョン
headers 空の配列 送るリクエストヘッダー
body null 送る本文(配列ならフォーム形式、文字列ならそのまま)
sslverify true SSL 証明書を検証するか
blocking true false にすると応答を待たずに戻る
reject_unsafe_urls false URL を検証して危険な宛先を拒否するか(wp_safe_ 系では true)
user-agent WordPress/バージョン; サイトURL 送る User-Agent

timeout の既定値5秒は、応答の遅い API では足りないことがあります。ただし長くしすぎると、API が止まったときにページの表示まで待たされます。ページ表示中に呼ぶなら10秒程度までにし、結果をキャッシュするのが現実的です。

GETリクエストでJSONを取得する

エラー処理まで含めた、使い回せる関数の例です。子テーマの functions.php か自作プラグインに置きます。

<?php
/**
 * 外部APIからJSONを取得して配列で返す。
 *
 * @param string $url  リクエスト先のURL。
 * @param array  $args wp_remote_get() に渡す追加の引数。
 * @return array|WP_Error
 */
function my_fetch_json( $url, $args = array() ) {
	$args = wp_parse_args(
		$args,
		array(
			'timeout' => 10,
			'headers' => array( 'Accept' => 'application/json' ),
		)
	);

	$response = wp_remote_get( $url, $args );

	// 1. 通信そのものの失敗(DNS、接続、タイムアウト、SSL など)
	if ( is_wp_error( $response ) ) {
		return $response;
	}

	// 2. HTTP ステータスの確認(404 や 500 は WP_Error にならない)
	$code = (int) wp_remote_retrieve_response_code( $response );
	if ( $code < 200 || $code >= 300 ) {
		return new WP_Error(
			'my_http_status',
			sprintf( 'HTTP %d が返りました。', $code ),
			array( 'status' => $code )
		);
	}

	// 3. 本文を JSON として解釈
	$data = json_decode( wp_remote_retrieve_body( $response ), true );
	if ( ! is_array( $data ) ) {
		return new WP_Error( 'my_http_json', '応答を JSON として読めませんでした。' );
	}

	return $data;
}

ポイントは、失敗の種類が2つあることです。

  • 通信の失敗: 接続できない、タイムアウトした、証明書が検証できないといった場合は WP_Error が返ります。$response->get_error_message() で理由が分かります。
  • HTTP のエラー: 404 や 500 は通信としては成功しているため、通常の配列で返ります。ステータスコードを自分で確認しないと、エラーページの HTML を JSON として読もうとしてしまいます。

クエリパラメーターを付けるときは、文字列を連結せずに add_query_arg() を使うと、URL の組み立て間違いを防げます。値に日本語や記号が入る場合は rawurlencode() で変換してから渡します。

$url = add_query_arg(
	array(
		'q'     => rawurlencode( '東京' ),
		'limit' => 10,
	),
	'https://api.example.com/v1/search'
);
$data = my_fetch_json( $url );

POSTリクエストでJSONを送る

body に配列を渡すと、application/x-www-form-urlencoded のフォーム形式で送られます。JSON を受け付ける API には、wp_json_encode() で文字列にし、Content-Type ヘッダーを付けて送ります。

function my_post_json( $url, array $payload, $token ) {
	$response = wp_remote_post(
		$url,
		array(
			'timeout' => 15,
			'headers' => array(
				'Content-Type'  => 'application/json',
				'Authorization' => 'Bearer ' . $token,
			),
			'body'    => wp_json_encode( $payload ),
		)
	);

	if ( is_wp_error( $response ) ) {
		return $response;
	}

	return array(
		'code' => (int) wp_remote_retrieve_response_code( $response ),
		'body' => json_decode( wp_remote_retrieve_body( $response ), true ),
	);
}

PUT や DELETE は wp_remote_request() の method で指定します。

$response = wp_remote_request(
	'https://api.example.com/v1/items/' . rawurlencode( $id ),
	array(
		'method'  => 'DELETE',
		'headers' => array( 'Authorization' => 'Bearer ' . $token ),
	)
);

APIキーやトークンの扱い

API キーやトークンは、コードに直接書かず wp-config.php の定数や環境変数に置き、コードからは定数名だけを参照します。

// wp-config.php(「編集が必要なのはここまで」の行より上)
define( 'MY_API_TOKEN', 'ここに発行されたトークン' );
$result = my_post_json( 'https://api.example.com/v1/items', array( 'name' => 'test' ), MY_API_TOKEN );

error_log() などでログを取るときは、エラーメッセージだけを書き、リクエストのヘッダーやトークンは記録しないでください。

sslverifyはfalseにしない

証明書エラーが出たときに 'sslverify' => false で回避する例を見かけますが、これは通信相手が本物かどうかを確かめない設定です。なりすましたサーバーにトークンや個人情報を送ってしまう恐れがあるため、本番では使いません。

証明書エラーの主な原因は次のとおりです。

  • 接続先の証明書の期限切れや、中間証明書の設定漏れ(接続先側で直す)
  • サーバーの CA 証明書が古い(OS のパッケージ更新で直す。WordPress は wp-includes/certificates/ca-bundle.crt も同梱しています)
  • サーバーの時計がずれている

ローカル開発環境の自己署名証明書だけが問題なら、その環境の wp-config.php か開発用プラグインに限って対応し、本番に持ち込まないようにします。

結果をトランジェントでキャッシュする

ページを表示するたびに外部 API を呼ぶと、表示が遅くなり、API の回数制限にもかかりやすくなります。トランジェントで一定時間保存し、その間は保存した値を使います。

function my_get_items_cached() {
	$key   = 'my_api_items_v1';
	$items = get_transient( $key );

	if ( false !== $items ) {
		return $items;
	}

	$items = my_fetch_json( 'https://api.example.com/v1/items' );

	if ( is_wp_error( $items ) ) {
		error_log( 'my_api: ' . $items->get_error_message() );
		// 失敗した結果は短時間だけ覚えて、API への連続アクセスを避ける
		set_transient( $key, array(), MINUTE_IN_SECONDS );
		return array();
	}

	set_transient( $key, $items, 15 * MINUTE_IN_SECONDS );
	return $items;
}

失敗したときも空の配列を1分だけ保存しているのは、API が落ちている間にアクセスのたびに再接続して、ページ表示が毎回タイムアウトまで待たされるのを防ぐためです。表示するときは、取得した値を esc_html() や esc_url() でエスケープしてから出力します。

更新頻度が低いデータなら、ページ表示中に取得せず、WP-Cron で定期的に取得してオプションに保存する方法もあります。

利用者が入力したURLにはwp_safe_remote_getを使う

フォームの入力など、利用者が指定した URL にサーバーからリクエストを送る場合は、wp_safe_remote_get() などの wp_safe_ 付きの関数を使います。reject_unsafe_urls が有効になり、127.0.0.1 やプライベートIPアドレスなど内部向けの宛先へのリクエストが拒否されます(SSRF と呼ばれる攻撃への対策です)。

$response = wp_safe_remote_get( $user_supplied_url, array( 'timeout' => 5 ) );
if ( is_wp_error( $response ) ) {
	// 内部向けの宛先は「A valid URL was not provided.」などのエラーになる
}

サーバー全体で外部への通信を制限する

wp-config.php に次の定数を書くと、WordPress の HTTP API からの外部リクエストをまとめて止め、許可したホストにだけ通信させられます。

define( 'WP_HTTP_BLOCK_EXTERNAL', true );
define( 'WP_ACCESSIBLE_HOSTS', 'api.wordpress.org,*.github.com,api.example.com' );

WP_ACCESSIBLE_HOSTS はカンマ区切りで、* を使ったワイルドカードも書けます。api.wordpress.org を外すと、本体・プラグインの更新確認ができなくなるため注意してください。

テストやデバッグで通信を差し替える

pre_http_request フィルターで配列を返すと、実際の通信をせずにその値が応答として使われます。外部 API に依存しない単体テストや、API 側が未完成のときの開発に使えます。

add_filter(
	'pre_http_request',
	function ( $pre, $args, $url ) {
		if ( 0 !== strpos( $url, 'https://api.example.com/' ) ) {
			return $pre; // 対象外の URL は通常どおり通信する
		}
		return array(
			'headers'  => array( 'content-type' => 'application/json' ),
			'body'     => wp_json_encode( array( 'items' => array( 'a', 'b' ) ) ),
			'response' => array( 'code' => 200, 'message' => 'OK' ),
			'cookies'  => array(),
			'filename' => null,
		);
	},
	10,
	3
);

また、すべてのリクエストの引数は http_request_args フィルターで、応答は http_api_debug アクションで確認できます。本番に残すとログが増えるため、調査が終わったら外してください。

うまくいかないときの確認

  1. cURL error 28(タイムアウト): timeout を延ばす前に、サーバーから接続先に到達できるか(ファイアウォールや送信制限)を確認します。
  2. cURL error 60 など証明書のエラー: 上の「sslverifyはfalseにしない」の項目を確認します。
  3. 401・403: 認証ヘッダーの形式(Bearer か Basic か)や、API キーの権限を確認します。
  4. ステータスは200なのにデータが空: wp_remote_retrieve_body() の中身を一時的にログに出し、JSON の構造とコードの参照しているキーが合っているかを確認します。
  5. 一部のサーバーだけ失敗する: WP_HTTP_BLOCK_EXTERNAL が設定されていないか、プロキシ設定(WP_PROXY_HOST など)が必要な環境でないかを確認します。

まとめ

  • 外部 API は wp_remote_get()・wp_remote_post()・wp_remote_request() で呼び、値は wp_remote_retrieve_*() で取り出す。
  • 通信の失敗は is_wp_error()、HTTP のエラーはステータスコードで、分けて確認する。
  • JSON を送るときは wp_json_encode() と Content-Type: application/json を使う。
  • sslverify は無効にせず、トークンは定数に置き、結果はトランジェントでキャッシュする。
  • 利用者が指定した URL には wp_safe_remote_get() を使う。

関連記事:

お気軽にご相談ください

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