2026年10月9日

2026年10月9日

WordPressのREST APIが401 Unauthorizedを返す原因と認証設定の直し方

WordPressのREST APIが401 Unauthorizedを返すのは、「WordPressがそのリクエストを、ログインしていない人からのものと判断した」という意味です。原因は、認証情報がそもそも送られていない、途中で消えている、送り方が認証方式に合っていない、のどれかであることがほとんどです。この記事では、401のレスポンスの読み方、Cookie認証とアプリケーションパスワードそれぞれの正しい設定、よくある原因を可能性の高い順に確認して直す手順を説明します。

401と403の違いを押さえる

WordPressのREST APIは、権限が足りないときに次のように使い分けます。

  • 401:ログインしていない(認証されていない)と判断された。
  • 403:ログインはしているが、その操作の権限が無い。

つまり401が返るなら、権限設定(ユーザーの役割)を見直す前に、認証そのものが成立しているかを疑います。

まず、レスポンスの本文を確認します。WordPressが返した401なら、本文はJSONで、code という項目に理由が入っています。

curl -i https://example.com/wp-json/wp/v2/users/me

認証なしでこのURLを開くと、401とともに code が rest_not_logged_in のJSONが返るのが正常です。認証付きで試して同じ結果なら、認証情報がWordPressに届いていません。

一方、本文がJSONではなくHTMLで、レスポンスヘッダーに WWW-Authenticate がある場合は、WordPressより手前のWebサーバーが401を返しています。この場合の原因はサーバー側のBASIC認証です(原因3)。

認証方式は目的で選ぶ

WordPress本体が標準で対応している認証方式は、主に次の2つです。

  • Cookie認証+nonce:WordPressにログインした状態のブラウザから、同じサイト上のJavaScriptでAPIを呼ぶ場合に使います。テーマやプラグインの画面からの操作が該当します。
  • アプリケーションパスワード:外部のプログラムやサービス、別サーバーからAPIを呼ぶ場合に使います。WordPress 5.6で本体に入りました。ユーザーのプロフィール画面で発行します。

JWTやOAuthを使うプラグインもありますが、まずは本体の仕組みで足りないかを検討してください。プラグインを使う場合は、更新が続いているか、利用者の多さとサポート状況を確認してから選びます。

原因1:Cookie認証でnonceを送っていない

ログインしているのに、自作のJavaScriptから投稿の作成や更新をすると401になる場合、最も多い原因がこれです。WordPressはCSRF対策として、Cookie認証のリクエストには X-WP-Nonce ヘッダー(値は wp_rest アクション用のnonce)を要求します。nonceが無いと、Cookieがあっても「ログインしていない」扱いになり、権限の必要な操作は401になります。nonceが不正または期限切れの場合は、403(rest_cookie_invalid_nonce)になります。

テーマやプラグインでスクリプトを読み込むときに、nonceとAPIのURLを渡します。

// 子テーマの functions.php など
add_action( 'wp_enqueue_scripts', function () {
	wp_enqueue_script(
		'my-app',
		get_stylesheet_directory_uri() . '/js/my-app.js',
		array(),
		'1.0.0',
		array( 'in_footer' => true )
	);
	wp_localize_script( 'my-app', 'myApp', array(
		'root'  => esc_url_raw( rest_url() ),
		'nonce' => wp_create_nonce( 'wp_rest' ),
	) );
} );
// js/my-app.js
fetch( myApp.root + 'wp/v2/posts', {
	method: 'POST',
	credentials: 'same-origin',
	headers: {
		'Content-Type': 'application/json',
		'X-WP-Nonce': myApp.nonce,
	},
	body: JSON.stringify( { title: 'テスト', status: 'draft' } ),
} );

スクリプトの依存に wp-api-fetch を指定して wp.apiFetch を使う方法もあります。この場合、WordPress本体がnonceを自動で付けるため、ヘッダーを自分で書く必要はありません。

つまずきやすいのは、ページキャッシュです。nonceを埋め込んだページがキャッシュされると、古いnonceや別ユーザー向けのnonceが配られ、時間がたつと失敗するようになります。ログインユーザーにはキャッシュを出さない設定になっているか確認します。

原因2:Authorizationヘッダーがサーバーで消えている

アプリケーションパスワードは、ユーザー名と発行したパスワードをBASIC認証の形式(Authorizationヘッダー)で送ります。

# ユーザー名とアプリケーションパスワードはダミー。実際の値に置き換える
curl -i -u 'your-username:xxxx xxxx xxxx xxxx xxxx xxxx' \
  'https://example.com/wp-json/wp/v2/users/me?context=edit'

正しい値なのに rest_not_logged_in が返る場合、ApacheのCGI/FastCGI構成などで、AuthorizationヘッダーがPHPに渡されていない可能性があります。.htaccess のWordPressのブロックに次の行があるか確認します。比較的新しいWordPressが生成する .htaccess には含まれていますが、古い環境から引き継いだファイルには無いことがあります。

RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

CDNやリバースプロキシ、WAFを経由している場合は、そこでAuthorizationヘッダーが落とされていないかも確認します。サーバー上で直接 curl を実行して成功するなら、手前の経路が原因です。

原因3:サイト全体にBASIC認証がかかっている

ステージング環境などで、Webサーバーの設定によりサイト全体にBASIC認証をかけていると、アプリケーションパスワードと同じAuthorizationヘッダーを取り合うことになります。サーバーのBASIC認証を通すとWordPressの認証に失敗し、WordPressの認証情報を送るとサーバーで401になります。

対処としては、/wp-json/ 配下だけサーバー側のBASIC認証を外して、かわりにIPアドレスで接続元を制限する方法がよく使われます。サーバーの設定変更になるため、変更前の設定ファイルを控えてから作業してください。

原因4:アプリケーションパスワードが使えない状態になっている

アプリケーションパスワードは、次の条件で使えなくなります。

  • サイトがHTTPSで動いていない(ローカル開発環境として設定されている場合を除く)。
  • セキュリティ系プラグインの設定で無効化されている。
  • テーマやプラグインが wp_is_application_passwords_available フィルターで無効にしている。

プロフィール画面にアプリケーションパスワードの欄が表示されない場合は、これらを疑います。また、送るユーザー名はログインに使うユーザー名で、表示名ではありません。パスワードは発行時に一度だけ表示されるもので、ログイン用のパスワードとは別物です。分からなくなったら、古いものを失効させて新しく発行します。

原因5:REST APIへのアクセスを制限するプラグインやコード

セキュリティ対策として「ログインしていないユーザーのREST APIを禁止する」機能を持つプラグインや、自作のコードがあると、条件によって401が返ります。こうした制限は rest_authentication_errors フィルターで実装されていることが多いため、テーマやプラグインのコードを検索すると見つけやすくなります。

grep -rn "rest_authentication_errors" wp-content/plugins wp-content/themes wp-content/mu-plugins

自作のカスタムエンドポイントの場合は、register_rest_route の permission_callback を確認します。この関数が false を返すと、ログインしていないリクエストには401が返ります。公開してよいエンドポイントなら __return_true を指定し、権限が必要なら current_user_can で判定します。

add_action( 'rest_api_init', function () {
	register_rest_route( 'myplugin/v1', '/private', array(
		'methods'             => 'GET',
		'callback'            => 'myplugin_private_data',
		'permission_callback' => function () {
			return current_user_can( 'edit_posts' );
		},
	) );
} );

安全に運用するための注意

  • アプリケーションパスワードをブラウザで動くJavaScriptやGitのリポジトリに書かないでください。サーバー側の環境変数や、Web公開されない場所の設定ファイルで管理します。
  • 連携ごとに別のアプリケーションパスワードを発行し、使わなくなったらプロフィール画面で失効させます。
  • 連携用には、必要な権限だけを持つ専用ユーザーを作ると、漏れたときの被害を抑えられます。
  • HTTPSでない経路では使わないでください。BASIC認証の形式は、暗号化されていないと中身を読まれます。

管理画面の項目名や対応状況は、WordPressのバージョンやプラグインによって変わることがあります(2026年10月現在)。

まとめ

  • 401は「ログインしていない」と判断された状態です。レスポンスがWordPressのJSONか、サーバーのHTMLかを最初に見分けます。
  • 同じサイト内のJavaScriptからはCookie認証で、X-WP-Nonce ヘッダーにnonceを付けて送ります。
  • 外部からはアプリケーションパスワードを使い、Authorizationヘッダーがサーバーや経路で消えていないかを確認します。
  • サイト全体のBASIC認証、アプリケーションパスワードの無効化、REST APIを制限するプラグインも401の原因になります。
  • 認証情報はコードに直接書かず、連携ごとに発行・失効できるようにしておきます。

お気軽にご相談ください

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