2026年6月30日

2026年6月30日

wp_remote_get()でBasic認証が必要なAPIを呼び出す方法(認証ヘッダーの付け方と401の切り分け)

wp_remote_get() で Basic 認証付きの API を呼ぶには、headers 引数に Authorization: Basic <ユーザー名:パスワードをBase64にした値> を入れます。WordPress の HTTP API には認証専用の引数が無いため、ヘッダーを自分で組み立てるのが基本です。この記事では、認証情報の置き場所、エラー処理とキャッシュまで含めた関数の書き方、401 や 403 が返るときの切り分け、Basic 認証で保護したステージングサイトへの内部リクエストの扱いを説明します。

認証情報はコードに直接書かない

ユーザー名とパスワードをテーマやプラグインのファイルに直接書くと、Git に記録されたり、テーマの配布や共有のときに漏れたりします。サイトごとに値が変わる秘密情報は、wp-config.php の定数(または環境変数)に置き、コードからは定数名だけを参照する形にします。

// wp-config.php(「編集が必要なのはここまで」の行より上に書く)
define( 'MY_API_USER', 'your-api-user' );
define( 'MY_API_PASS', 'your-api-password' );

wp-config.php を編集する前にはファイルのバックアップを取ってください。環境変数を使う場合は getenv( 'MY_API_PASS' ) で読み出せます。どちらの場合も、値の前後に空白や改行が混ざると認証に失敗するので注意が必要です。

Basic 認証はユーザー名とパスワードを Base64 にしただけで、暗号化ではありません。通信が HTTPS でなければ途中で読み取られるため、https:// の API にだけ使ってください。

呼び出し用の関数を作る

子テーマの functions.php か、自作プラグイン(または mu-plugin)に次の関数を置きます。テーマを変えても使い続ける処理なら、プラグイン側に置く方が適切です。

<?php
/**
 * Basic認証付きAPIにGETリクエストを送り、JSONを配列で返す。
 *
 * @param string $path  APIのパス(例: '/v1/items')
 * @param array  $query クエリパラメーター
 * @return array|WP_Error
 */
function my_api_get( $path, $query = array() ) {
	if ( ! defined( 'MY_API_USER' ) || ! defined( 'MY_API_PASS' ) ) {
		return new WP_Error( 'my_api_config', 'API認証情報が設定されていません。' );
	}

	$url = add_query_arg(
		array_map( 'rawurlencode', $query ),
		'https://api.example.com' . $path
	);

	$response = wp_remote_get(
		$url,
		array(
			'timeout'     => 10,
			'redirection' => 0,
			'headers'     => array(
				'Authorization' => 'Basic ' . base64_encode( MY_API_USER . ':' . MY_API_PASS ),
				'Accept'        => 'application/json',
			),
		)
	);

	if ( is_wp_error( $response ) ) {
		return $response; // 接続失敗・タイムアウト・SSLエラーなど
	}

	$code = (int) wp_remote_retrieve_response_code( $response );
	if ( 200 !== $code ) {
		return new WP_Error( 'my_api_http', sprintf( 'APIがHTTP %d を返しました。', $code ), array( 'status' => $code ) );
	}

	$data = json_decode( wp_remote_retrieve_body( $response ), true );
	if ( null === $data && JSON_ERROR_NONE !== json_last_error() ) {
		return new WP_Error( 'my_api_json', 'APIの応答をJSONとして読めませんでした。' );
	}

	return $data;
}

各引数の意味

  • timeout: 既定は5秒です。応答の遅い API ではタイムアウトしやすいため、API の性質に合わせて調整します。長くしすぎると、API が止まったときにページ表示まで待たされます。
  • redirection: リダイレクトをたどる回数です。認証ヘッダーを付けたままリダイレクト先へ送らないよう、ここでは0にして、3xx が返ったら URL 自体を正しいものに直す方針にしています。
  • sslverify: 既定は true(証明書を検証する)です。証明書エラーを避けるために false にすると、なりすましたサーバーへ認証情報を送ってしまう恐れがあるため、変えないでください。

wp_remote_get() は、通信そのものが失敗したときは WP_Error を返し、HTTP のエラー(401 や 500)は通常の応答として返します。両方を分けて確認しているのはこのためです。

結果をキャッシュして表示する

ページ表示のたびに外部 API を呼ぶと、表示が遅くなるうえ API の回数制限にもかかりやすくなります。トランジェントで一定時間キャッシュし、表示時はエスケープしてから出力します。

function my_api_items_cached() {
	$cache_key = 'my_api_items_v1';
	$items     = get_transient( $cache_key );

	if ( false === $items ) {
		$items = my_api_get( '/v1/items', array( 'limit' => 10 ) );
		if ( is_wp_error( $items ) ) {
			error_log( 'my_api: ' . $items->get_error_message() ); // 認証情報はログに書かない
			return array();
		}
		set_transient( $cache_key, $items, 10 * MINUTE_IN_SECONDS );
	}

	return $items;
}

// 表示例
foreach ( my_api_items_cached() as $item ) {
	echo '<li>' . esc_html( $item['name'] ?? '' ) . '</li>';
}

エラー時に error_log() へ書くのはエラーメッセージだけにして、リクエストのヘッダーや認証情報そのものは記録しないでください。デバッグ中に $response 全体をログへ出力するのも、ヘッダーが含まれる場合があるため避けます。

他のWordPressサイトをアプリケーションパスワードで呼ぶ

連携先が WordPress の場合は、そのサイトのユーザープロフィール画面で発行できる「アプリケーションパスワード」が Basic 認証の形式で使えます。ログイン用のパスワードではなく、連携専用に発行した値を MY_API_PASS に入れます。

$me = my_api_get( '/wp-json/wp/v2/users/me', array( 'context' => 'edit' ) );

ベースURLを連携先サイトに変えて実行し、ユーザー情報が返れば認証できています。アプリケーションパスワードは原則として HTTPS のサイトで有効になる機能で(ローカル開発環境などは例外)、不要になったら発行元の画面から個別に取り消せます。

401・403が返るときの切り分け

よく起きる原因から順に確認します。

  1. 認証情報の誤り: 定数の値に空白や改行が入っていないか、ユーザー名とパスワードを取り違えていないかを確認します。ユーザー名にコロン(:)を含めることは Basic 認証の仕様上できません。同じ値で curl -u を使って直接試すと、WordPress 側の問題かどうかを切り分けられます。
  2. 認証方式が違う: API が Bearer トークンや Digest 認証を求めている場合、Basic のヘッダーでは通りません。応答ヘッダーの WWW-Authenticate を wp_remote_retrieve_header( $response, 'www-authenticate' ) で確認すると、求められている方式が分かります。
  3. 連携先のサーバーがヘッダーをPHPに渡していない: 連携先が WordPress で、Apache の CGI/FastCGI 構成の場合、Authorization ヘッダーが PHP まで届かず 401 になることがあります。連携先の .htaccess に、このヘッダーを環境変数として渡す設定(近年の WordPress が生成する標準の .htaccess には HTTP_AUTHORIZATION を渡す行が含まれています)があるかを確認します。
  4. 二重の Basic 認証: 連携先のサイト全体が Webサーバーの Basic 認証で保護されている場合、Authorization ヘッダーは1つしか送れないため、アプリケーションパスワードと両立しません。API のパスだけ Webサーバーの認証から除外するなどの調整が必要です。
  5. 403 Forbidden: 認証は通っていても権限が足りない、または WAF や IP 制限で拒否されている状態です。API 側の権限設定や、接続元IPの許可リストを確認します。

Basic認証をかけたステージングへの内部リクエスト

ステージングサイト全体を Webサーバーの Basic 認証で保護すると、WP-Cron の起動やサイトヘルスのループバック確認など、WordPress が自分自身へ送るリクエストが 401 で失敗することがあります。HTTP API のすべてのリクエストを通る http_request_args フィルターで、自サイト宛てのときだけヘッダーを足す方法があります。

add_filter(
	'http_request_args',
	function ( $args, $url ) {
		if ( ! defined( 'STAGING_BASIC_USER' ) || ! defined( 'STAGING_BASIC_PASS' ) ) {
			return $args;
		}
		$site_host = wp_parse_url( home_url(), PHP_URL_HOST );
		if ( wp_parse_url( $url, PHP_URL_HOST ) === $site_host ) {
			$args['headers']['Authorization'] = 'Basic ' . base64_encode( STAGING_BASIC_USER . ':' . STAGING_BASIC_PASS );
		}
		return $args;
	},
	10,
	2
);

ホスト名を比較しているのは、外部のサーバーへステージングの認証情報を送らないためです。本番に持ち込まないよう、定数はステージングの wp-config.php にだけ定義してください。Webサーバー側でサーバー自身のIPからのアクセスを認証の対象外にする方法でも解決できます。

なお、呼び出し先の URL の一部を利用者の入力から組み立てる場合は、wp_remote_get() の代わりに wp_safe_remote_get() を使うと、社内ネットワークなどへの意図しないリクエストを防ぎやすくなります。HTTP API の細かな挙動は WordPress の版によって変わることがあるため(2026年10月現在)、更新後は連携処理の動作を確認してください。

まとめ

  • Basic 認証は headers の Authorization に 'Basic ' . base64_encode( 'ユーザー名:パスワード' ) を入れて送る。
  • 認証情報は wp-config.php の定数か環境変数に置き、HTTPS の API にだけ使う。sslverify は無効にしない。
  • is_wp_error() で通信エラーを、wp_remote_retrieve_response_code() で HTTP エラーを分けて扱い、結果はトランジェントでキャッシュする。
  • 401 は認証情報、認証方式、連携先のヘッダー受け渡し、二重認証の順に確認する。
  • Basic 認証で保護したステージングでは、http_request_args で自サイト宛てのときだけヘッダーを足す。

お気軽にご相談ください

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