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の原因になります。
- 認証情報はコードに直接書かず、連携ごとに発行・失効できるようにしておきます。