WordPress REST API の認証を通す — Application Passwords と、401が返るときの切り分け

WordPress REST API の認証を通す — Application Passwords と、401が返るときの切り分け

先に結論

「先に結論」の内容を図にまとめたもの

WordPress の REST API に認証を通す方法は、昔はプラグインを選ぶところから始まりました。 いまは選ぶ必要がありません。用途が2つに分かれていて、それぞれ答えが1つです。

表1:どこから呼ぶかで、使う認証が決まる
呼び出し元使うものプラグイン
サーバー・スクリプト・外部アプリ(cron、CLI、別サーバー)Application Passwords(Basic認証)不要。WordPress 5.6からコア同梱
同じサイトのブラウザ内(管理画面のJS、テーマ、プラグイン)Cookie + nonce(X-WP-Nonce)不要
上記以外で、どうしても必要な場合OAuth 1.0a / JWT のプラグイン必要。ただし先に上2つを検討する

そして、401が返ったときの切り分けはこうです。

表2:401が返ったときに最初に見るもの
応答の code意味疑うところ
rest_forbidden権限がない認証は通っているが、そのユーザーに権限がない
rest_not_logged_in未認証として扱われているAuthorizationヘッダが届いていない(詳しく)
incorrect_passwordパスワードが違うログインパスワードを使っていないか
application_passwords_disabled機能が無効セキュリティプラグイン、またはHTTPSでない
rest_no_route(404)ルートが見つからないURLかメソッド違い。エンドポイントが塞がれている場合も

検索すると「OAuth 2 のプラグインを入れる」という解説が出てきますが、 WordPress公式が認証プラグインとして挙げているのは OAuth 1.0a と JWT で、OAuth 2 ではありません。 コアが用意したのは Application Passwords です。 いまから OAuth 2 のサーバーを立てる必要は、ほぼありません。

選択肢は実質2つしかない

「選択肢は実質2つしかない」の内容を図にまとめたもの

Application Passwords はコアの機能

公式ドキュメントにはこう書かれています。 「5.6 以降、WordPress には Application Passwords が同梱されており、 ユーザー編集画面(wp-admin → ユーザー → ユーザーを編集)から生成できます」。

発行したものは HTTPS 経由の Basic認証(RFC 7617)でREST APIに渡します。 プラグインのインストールも、Client ID の取得も、有料ライセンスも要りません。

認証プラグインを入れる話は、ほぼ終わった

公式ドキュメントは認証プラグインとして OAuth 1.0a Server と JSON Web Tokens を挙げています。 そして Basic Authentication プラグインについては、はっきりこう書いています。

「このプラグインはリクエストごとにユーザー名とパスワードの送信を必要とするため、 開発とテストでのみ使用すべきであり、本番環境では使うべきではありません。 上記の Application Passwords を使うほうが望ましいです」
— Authentication(REST API Handbook・要約)

表3:公式が挙げている認証手段
手段コアに入っているか公式の位置づけ
Cookie + nonce入っているプラグイン・テーマ開発での推奨
Application Passwords入っている(5.6〜)外部アプリからの推奨
OAuth 1.0a Serverプラグイン代替手段として紹介
JSON Web Tokensプラグイン代替手段として紹介
Basic Authentication プラグインプラグイン開発・テスト専用。本番で使うな
OAuth 2—公式の一覧に無い

使えるかどうかは、1回のGETで分かる

「使えるかどうかは、1回のGETで分かる」の内容を図にまとめたもの

authentication キーを見る

インストールも設定も要りません。REST APIのルートURLを叩いて、authentication キーを見るだけです。 公式の統合ガイドがこの方法を案内しています。「空なら Application Passwords は使えません」。

curl -s https://example.com/wp-json/ | jq .authentication

使える状態なら、こう返ってきます。

{
  "application-passwords": {
    "endpoints": {
      "authorization": "https://example.com/wp-admin/authorize-application.php"
    }
  }
}

{}(空のオブジェクト)が返ったら使えません。 HTTPSでないか、プラグインで無効化されているか、のどちらかです。

運用中の11サイトで叩いてみた

手元で運用しているWordPressサイト11件に対して、実際に /wp-json/ をGETしました。 どれも公開エンドポイントへの読み取りだけで、認証も書き込みもしていません。

表4:11サイトの実測結果(2026-10-25)
結果件数内容
HTTP 200・application-passwords あり9そのまま使える
HTTP 404・HTMLが返る1静的書き出しのサイト。WordPress本体は別ドメインにある
接続できず(タイムアウト)1サイト全体に到達できず。APIの問題ではない

9件はすべて、何も設定していないのに使える状態でした。 テーマもプラグイン構成もバラバラで、共通していたのはHTTPSで動いていることだけです。 「使えるようにする作業」はほぼ不要で、確認するだけで済みます。

使えなかった2件の理由

残り2件は、原因がまったく別でした。

表5:使えなかったケースの切り分け
症状原因対処
/wp-json/ が404でHTMLを返すそのドメインは静的HTMLの書き出しで、WordPressが動いていない本体が動いているドメインを叩く
接続自体ができないサイトが落ちているか、アクセスを弾かれているトップページも叩いて切り分ける

1件目は、静的書き出しのサイトでよく踏みます。 見た目はWordPressのサイトなのに、公開されているのは書き出されたHTMLだけなので、 /wp-json/ は単なる存在しないパスになります。

このとき返ってくるのは404のHTMLです。それをJSONとして解釈しようとすると、こうなります。

SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

手元で実際に出した出力です。「APIが壊れている」のではなく、そこにAPIが無いだけです。 このエラーの読み方は Unexpected token '<' ... is not valid JSON の原因と直し方 に書きました。

2件目は、トップページも叩いて切り分けました。 トップページも応答しないなら、APIの話ではありません。 /wp-json/ だけが失敗するのか、サイト全体が失敗するのかを先に分けてください。

Application Passwords を使う

「Application Passwords を使う」の内容を図にまとめたもの

発行する

wp-admin → ユーザー → 自分のプロフィールを開き、下のほうにある 「アプリケーションパスワード」で名前を付けて発行します。 表示されるのはその1回だけなので、その場で控えます。

表6:発行されるパスワードの仕様(公式)
項目内容
長さ24文字
使われる文字大文字・小文字・数字。記号は含まれない
表示形式abcd EFGH 1234 ijkl MNOP 6789 のように区切って表示される
スペースの扱いスペースを抜いて使ってもよい
管理できる人ユーザーを編集できる権限があれば誰でも(細分化は今後の予定)
前提SSL/HTTPSで配信されているサイト

表示のスペースは読みやすさのためのもので、入れても抜いても通ります。 ここで迷って「コピーミスかもしれない」と何度も発行し直す必要はありません。

HTTPSが前提なのは仕様です。公式はこう書いています。 「SSLがなければ、ネットワーク上の攻撃者にアプリケーションパスワードを見られる可能性があります」。 Basic認証は、中身をほぼそのまま送っているだけだからです。

curlで叩く

公式に載っている形です。ユーザー名とアプリケーションパスワードを --user に渡します。

# 公式ドキュメントの例
curl --user "USERNAME:PASSWORD" https://HOSTNAME/wp-json/wp/v2/users?context=edit

# 実際に使う形(スペースは抜いてよい)
curl --user "subburi:abcdEFGH1234ijklMNOP6789" \
  https://example.com/wp-json/wp/v2/posts?context=edit&per_page=1

投稿するときは、ヘッダとJSONを付けてPOSTします。

curl -X POST \
  --user "subburi:abcdEFGH1234ijklMNOP6789" \
  -H "Content-Type: application/json" \
  -d '{"title":"テスト","content":"本文","status":"draft"}' \
  https://example.com/wp-json/wp/v2/posts

"status":"draft" を付けておくのが安全です。 省略すると既定値で作られます。最初の1回は下書きで試して、応答の id と link を確認してください。

管理画面のJSやテーマから同じサイトのAPIを呼ぶ場合は、話が変わります。 ログイン中のCookieがすでにあるので、認証そのものは済んでいます。 足りないのはCSRF対策のnonceです。

公式の説明で、一番効くのはこの一文です。

「nonceが渡されなかった場合、APIは現在のユーザーを 0 に設定し、 WordPressにログインしていてもそのリクエストを未認証リクエストとして扱います」
— Authentication(REST API Handbook・要約)

ログインしているのに401が返る、という症状の多くはこれです。 エラーは「権限がない」ように見えますが、WordPress側から見れば未ログインの人が叩いています。

渡し方は、PHP側でnonceを出して、JS側でヘッダに載せます。

<?php
// PHP側:スクリプトにnonceを渡す(公式の例)
wp_localize_script( 'wp-api', 'wpApiSettings', array(
    'root'  => esc_url_raw( rest_url() ),
    'nonce' => wp_create_nonce( 'wp_rest' )
) );
// JS側:X-WP-Nonce ヘッダに載せる
fetch( wpApiSettings.root + 'wp/v2/posts/1', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-WP-Nonce': wpApiSettings.nonce,
  },
  credentials: 'same-origin',
  body: JSON.stringify( { title: 'Hello Moon' } ),
} );
表7:nonceの渡し方
方法書き方評価
ヘッダX-WP-Nonce最も確実。これを使う
クエリ / POSTデータ_wpnonce動くが、DELETEでは不安定

公式が「ヘッダが最も確実」と書いているのは DELETE のためです。 PHPは DELETE のリクエストボディをスーパーグローバルに展開しません。 クエリやボディに入れる方式だと、DELETE のときだけ落ちます。

nonceのアクション名は wp_rest 固定です。 自分のエンドポイントの中でnonceを検証し直す必要はありません。 WordPressが rest_cookie_check_errors() で自動的にやってくれます。

ブラウザに配るJSにアプリケーションパスワードを書かないでください。 ソースを見れば誰でも読めます。アプリケーションパスワードは サーバー側のコードからだけ使うものです。 ブラウザから呼ぶなら、使うのはCookieとnonceです。

401が返るときの切り分け

「401が返るときの切り分け」の内容を図にまとめたもの

まずエラーコードを読む

WordPressのREST APIは、失敗の理由をJSONの code で返します。 HTTPステータスだけ見ていると区別がつきません。本文を読んでください。

手元のサイトで、認証なしのまま各エンドポイントを叩いた結果です。

表8:認証なしで叩いたときの実測応答
叩いたものHTTPcode読み方
/wp/v2/posts200—公開エンドポイント。認証なしで読める
/wp/v2/settings401rest_forbidden保護されている。認証が要る
/wp/v2/users?context=edit404rest_no_routeそのルート自体が無い

3行目が要注意です。公式ドキュメントに載っているURLなのに 404 が返りました。 認証の問題ではなく、そのサイトでユーザー一覧のエンドポイントが塞がれているということです。 セキュリティプラグインや functions.php での対策としてよく行われます。

rest_no_route が返ったら、認証を疑う前にエンドポイントの存在を疑ってください。 いくら正しいパスワードを渡しても404のままです。

Authorizationヘッダが落ちている

パスワードは合っているのに401が返り続ける場合、最有力はこれです。 公式のFAQに項目があります。

「認証ヘッダを送っているのにリクエストが受け付けられず、CGI環境を使っている場合、 Webサーバーがヘッダを取り除いている可能性があります。 以下の設定を追加して解決を試みてください」
— Frequently Asked Questions(REST API Handbook・要約)

公式が載せている設定です。

# Apache(.htaccess または設定ファイル)
<IfModule mod_setenvif>
  SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
</IfModule>
# Nginx(fastcgi の設定セクション)
fastcgi_pass_header Authorization;
表9:ヘッダが落ちやすい構成
構成対処
PHPがCGI / FastCGIで動いている(mod_phpでない)上のApache設定を # BEGIN WordPress より前に書く
Nginx + PHP-FPMfastcgi_pass_header Authorization;
Nginxが前段、Apacheが後段両方に設定が要る場合がある
共有レンタルサーバーで設定ファイルを触れない.htaccessで試す。だめならREST APIを諦める判断も要る

この症状の見分け方は、エラーコードです。 ヘッダが届いていなければ、WordPressから見れば誰も名乗っていないので rest_not_logged_in 系になります。 パスワードが違うなら incorrect_password が返るので、そこで区別できます。

それでも通らないとき

表10:残りの原因
確認すること症状
プロフィール画面にアプリケーションパスワードの項目があるか無ければ、セキュリティプラグインが隠している
/wp-json/ の authentication が空でないか空なら機能が無効(確認方法)
HTTPSで叩いているかhttpだと既定で使えない
ログインパスワードを使っていないかアプリケーションパスワードは別物
そのユーザーに権限があるか投稿するなら投稿できる権限が要る
アプリケーションパスワードをプラグインで入れていないかコアの機能と競合することがある

1行目を先に見てください。プロフィール画面に項目が出ていないなら、そこで話が終わります。 curlをいくら書き直しても通りません。

公開エンドポイントは誰でも叩ける

「公開エンドポイントは誰でも叩ける」の内容を図にまとめたもの

認証を整える前に、知っておくべきことがあります。 WordPressのREST APIは、リクエストのOriginヘッダを検証しません。

公式FAQの説明はこうです。

CORSの制限を強めると一部の認証方法が使えなくなるため、 WordPressのREST APIはCORSではなくnonceをCSRF対策として使っている。 Originヘッダを検証しないので、公開REST APIエンドポイントはどのサイトからでもアクセスできる。
— Frequently Asked Questions(REST API Handbook・要約)

これは設計上の意図であって、不具合ではありません。 ただし、公開したくないものが公開エンドポイントから読めていないかは、確認する価値があります。

表11:確認しておくエンドポイント
エンドポイント見えるもの考えること
/wp/v2/usersユーザー名・スラッグログインIDの推測材料になる。塞いでいるサイトが多い
/wp/v2/posts公開済みの投稿公開情報なので基本は問題ない
/wp/v2/pages公開済みの固定ページ同上
/wp/v2/mediaメディアライブラリの項目記事に貼っていない画像も見える
/wp-json/有効なプラグインの名前空間使っているプラグインが推測できる

最後の行は、今回11サイトを叩いたときに実感しました。 /wp-json/ の namespaces を見るだけで、 SEOプラグイン、問い合わせフォーム、リダイレクト管理など、入れているものがかなり分かります。 塞ぐかどうかは別として、見えているという事実は知っておいたほうがいいです。

AIに任せるときの注意

「AIに任せるときの注意」の内容を図にまとめたもの
表12:AIへの頼み方
頼むこと向くか理由
「Application Passwordsで投稿するスクリプトを書いて」向く定型。Basic認証とJSONを組むだけ
curlの応答(code 付き)を貼って「原因は?」向くエラーコードから切り分けられる
/wp-json/ の出力を貼って「使える?」向くauthentication を見るだけ
「WordPress REST APIの認証方法を教えて」向かないOAuth 2プラグインの話が返る。5.6以前の情報が大量にある
「401が出ます、直して」(情報なし)向かない原因が5つ以上ある。切り分けを先にやる
サーバー設定の修正を任せる向かない.htaccessの書き換えは壊すと全ページ落ちる。必ず控えを取る

判定は簡単です。「プラグインをインストールして Client ID を取得して」と言い出したら、情報が古いと思ってください。 2026年の答えは、プロフィール画面でパスワードを1つ発行するだけです。

よくある質問

OAuth 2 のプラグインは必要ですか

ほぼ不要です。公式が認証プラグインとして挙げているのは OAuth 1.0a と JWT で、OAuth 2 ではありません(表3)。 外部アプリから叩くだけなら Application Passwords で足ります。

アプリケーションパスワードの項目が見当たりません

HTTPSでないか、セキュリティプラグインが隠しています。 /wp-json/ の authentication が空かどうかで確認できます(詳しく)。

パスワードのスペースは入力するのですか

どちらでも通ります。公式が「スペースなしでも使える」と明記しています(表6)。 表示の区切りは読みやすさのためのものです。

ログインしているのに401が返ります

ブラウザ内から呼んでいるなら、nonceが渡っていません。 公式は「nonceが無ければ現在のユーザーを 0 に設定し、ログインしていても未認証リクエストとして扱う」と書いています(詳しく)。 X-WP-Nonce ヘッダを付けてください。

パスワードは合っているのに401が返ります

Authorizationヘッダがサーバーで落とされている可能性が高いです(詳しく)。 CGI/FastCGI環境でよく起きます。公式FAQの設定を入れてください。 応答の code が incorrect_password ならパスワードの問題、 未認証扱いの系ならヘッダの問題です。

404と rest_no_route が返ります

認証の前に、そのエンドポイントが存在しません。 URLかメソッドの間違いか、プラグイン等でそのルートが塞がれています(表8)。 実際、公式ドキュメントに載っているユーザー一覧のURLでも404になるサイトがあります。

/wp-json/ を叩いたらHTMLが返ります

そのドメインでWordPressが動いていません。静的に書き出したサイトでよくあります(表5)。 それをJSONとして読むと Unexpected token '<' になります。 詳しくは こちら。

DELETEだけ失敗します

nonceをクエリやボディで渡していませんか。 PHPは DELETE のリクエストボディをスーパーグローバルに展開しません。 X-WP-Nonce ヘッダで渡してください(表7)。

ブラウザのJSにアプリケーションパスワードを書いてもいいですか

いけません。ソースを見れば誰でも読めます。 サーバー側のコードからだけ使ってください。ブラウザから呼ぶならCookieとnonceです。

まとめ

/wp-json/ がHTMLを返したときのエラーは Unexpected token '<' ... is not valid JSON の原因と直し方、 ブラウザから叩くJSの話は Vue 3 でセレクトボックスからページ遷移する と localStorage の使い方、 SPAでの計測は GA4でSPAのページビューが計測されない5つの原因 に書きました。

11サイトの実測は2026年10月25日に、公開されている /wp-json/ へのGETだけで行ったものです (認証も書き込みもしていません)。サイト名は伏せています。 仕様とプラグイン構成は変わります。自分のサイトで同じGETを1回叩いて確認してください。 .htaccess を書き換えるときは、必ず控えを取ってからにしてください。書き損じると全ページが落ちます。