WordPressのアプリケーションパスワードで401になる原因|権限・Basic認証・ヘッダ落ちの切り分け順
WordPressのアプリケーションパスワードで401になる原因|権限・Basic認証・ヘッダ落ちの切り分け順
WordPressの管理画面でアプリケーションパスワードを発行し、REST APIに接続しようとして 401 が返る。ユーザー名もパスワードも合っているはずなのに通らない。この状態で最初にやってしまいがちなのが、パスワードの作り直しです。
結論から書きます。401が返ったとき、パスワードを再発行する前に見るのはレスポンスの中身に入っているエラーコードです。 401という数字は同じでも、原因は大きく2種類に分かれ、直す場所がまったく違います。
rest_not_logged_in… 認証情報がWordPressまで届いていない。パスワードの正しさとは無関係な場合があるrest_cannot_create… 認証は通っている。ユーザーの権限が足りていない
この2つを先に分けると、切り分けは4か所に絞られます。①ユーザーの権限 ②サイト側ではなくサーバー側のBasic認証 ③サーバーがAuthorizationヘッダを落としている ④パスワードの失効。この順で見ていけば、当てずっぽうの再発行を繰り返さずに原因までたどり着けます。
まず切り分ける:401の2種類
REST APIが401を返すとき、レスポンスのJSONには必ず code が入っています。curlでもスクリプトでも、HTTPステータスだけを見て終わりにせず、本文を出力するようにしておきます。
| エラーコード | 意味 | 見る場所 |
|---|---|---|
rest_not_logged_in |
WordPressが「誰からのリクエストか」を認識できていない | Authorizationヘッダの到達、Basic認証の重複、パスワードの失効 |
rest_cannot_create |
認証は成功。そのユーザーに投稿権限がない | ユーザーの権限(購読者・寄稿者になっていないか) |
rest_forbidden |
認証は成功。その操作が許可されていない | 権限、対象リソースの所有者 |
rest_not_logged_in が返っているときにパスワードを作り直しても、多くの場合は何も変わりません。 WordPressはパスワードを照合する前の段階で止まっているからです。逆に rest_cannot_create が返っているなら認証そのものは成立しているので、パスワードは触らず権限だけを直します。
このコードの読み分けを最初に置くだけで、原因の候補が半分になります。
接続の確認は「投稿」ではなく「疎通」で行う
もうひとつ、最初に決めておきたいことがあります。接続確認は、記事の投稿で試さないことです。
投稿APIは、認証・権限・本文の整形・カテゴリID・アイキャッチのアップロードが同時に走ります。ここでエラーが出ると、認証で落ちたのか、渡したデータで落ちたのかが混ざって分かりません。
確認用に叩くのは、認証だけを見る次のエンドポイントです。
GET /wp-json/wp/v2/users/me?context=edit
これが 200 を返せば、認証は成立しています。認証が通ったことを確認してから、初めて投稿の中身を組み立てます。 自動投稿の仕組みを作るときは、この疎通確認を doctor のような独立コマンドとして最初に用意しておくと、以降の障害切り分けが一段速くなります。
あわせて、アプリケーションパスワード機能そのものが有効かどうかも確認できます。
GET /wp-json/
このレスポンスの authentication に application-passwords が含まれていれば、機能は有効です。ここに出ているのに401が返る場合、機能の無効化ではなく別の原因だと確定できます。 「機能が使えていないのかもしれない」という疑いを1回で消せるので、確認の価値があります。
実例:パスワードを作り直しても直らず、原因はサーバー側だった
自社ブログの自動投稿の仕組みを作ったときの記録です。この事例は、切り分け順がそのまま役に立った形になっています。
症状:接続診断は 401 rest_not_logged_in。投稿を試すと 401 rest_cannot_create。正しいユーザー名とアプリケーションパスワードを使っており、curl -u でもAuthorizationヘッダをBasicで明示しても通らない。
このとき順番に確認したのは次のとおりです。
- 新しいアプリケーションパスワードを発行して再試行。管理画面に表示される4文字区切りのパスワードは、空白を入れたまま使う形と、空白を除去した形の両方を試した。結果は変わらず
rest_not_logged_in /wp-json/を確認。authentication.application-passwordsの記載あり。機能自体は有効と確定- 代替手段の確認。XML-RPC経由も検討したが、
/wp/xmlrpc.phpは404、ドキュメントルート直下の/xmlrpc.phpは参照先エラーで壊れており使えない状態だった
ここまでで「パスワードの誤りではない」「WordPressの機能でもない」と言える状態になりました。残るのは、認証情報がWordPressに届く前に消えているという可能性です。
原因:このサーバーはPHPをFastCGI(CGI)方式で動かしており、ApacheがAuthorizationヘッダをPHPへ渡さずに落としていました。WordPressはBasic認証の情報を受け取れないため、正しいパスワードを送っていても「ログインしていないリクエスト」として扱われます。
対処:WordPressを設置したディレクトリの .htaccess に、ヘッダを透過させる記述を追加します。
<IfModule mod_setenvif.c>
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP:Authorization} .
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
</IfModule>
この記述を置く位置が重要です。# BEGIN WordPress から # END WordPress で囲まれたブロックの中ではなく、その外側(ファイルの先頭)に書きます。 WordPress本体やプラグインがパーマリンク設定を書き換えるとき、書き換えの対象になるのはBEGIN/ENDで囲まれた区間だけです。ブロックの内側に置くと、更新のタイミングで消えて、ある日突然401に戻ります。
実際に、パーマリンク再構築の処理(flush_rewrite_rules)を走らせたあとに透過ルールが保持され、APIが200を返し続けることを確認しています。位置を守っていれば、WordPressの自動更新でも消えません。
同じサーバー会社に設置した別ドメインのブログでも、まったく同じ症状が出て、同じ対処で解決しました。サーバーの実行方式に起因する問題なので、そのサーバーに立てるWordPressすべてで同じ対応が必要になります。
一方で、別のサーバー会社に設置した美容室のサイトでは、アプリケーションパスワードを発行しただけで一発で通りました。つまりこれは「WordPressの設定ミス」ではなく、サーバー環境の差です。 手元でうまくいった手順が別の案件で通らないのは、この差が理由であることが多いです。
切り分けの手順
ここまでを、実行する順番に並べます。上から順に潰すと、無駄な作業が発生しません。
ステップ1:エラーコードを確認する
レスポンス本文の code を見ます。rest_cannot_create や rest_forbidden ならステップ4(権限)へ飛びます。rest_not_logged_in ならステップ2へ進みます。
ステップ2:サイト側の401か、サーバー側の401かを分ける
見落としやすいのがここです。制作中のサイトにサーバー側のアクセス制限(Basic認証)をかけている場合、そのサイトのURLはすべて401を返します。 WordPressのREST APIも例外ではありません。
このときの401は、WordPressのアプリケーションパスワードとは無関係です。サーバーのアクセス制限が、リクエストをWordPressに届く前で止めています。
判定は簡単です。ブラウザでサイトのトップページを開いたときにIDとパスワードを聞かれるなら、サーバー側の制限がかかっています。 公開前のサイトで自動投稿を組む場合は、以下のいずれかで進めます。
- アクセス制限用の認証情報を、APIリクエスト側にも渡す
- 制限を一時的に外して疎通確認だけ済ませる(外している間に検索エンジンに拾われないよう、noindexの設定は残しておく)
制作中のサイトでは「認証なしで401が返る/認証を通すと200が返る」という状態が正常です。この確認をアクセス制限の解除前後で1回ずつ行っておけば、公開後にどちらの401だったのか迷いません。
ステップ3:Authorizationヘッダの到達を確認する
ステップ2で「サーバー側の制限はかかっていない」と分かり、それでも rest_not_logged_in が返るなら、ヘッダの落下を疑います。前述の透過ルールを .htaccess のWordPressブロック外に追加し、users/me?context=edit で200を確認します。
.htaccess を編集したあとは、サイトのフロント側が200のままかを必ず確認します。 記述の書き方を誤ると、サイト全体が500エラーになります。編集前のファイルは必ず控えを取っておきます。
ステップ4:ユーザーの権限を確認する
rest_cannot_create の場合はここだけの問題です。アプリケーションパスワードは、発行したユーザーの権限をそのまま引き継ぎます。 購読者や寄稿者の権限で発行したパスワードでは、記事の公開はできません。
自動投稿に使うなら、投稿権限のあるユーザーで発行します。運用としては、既存の管理者アカウントを使い回すより、用途ごとに名前を付けたパスワードを発行しておく方が管理しやすくなります。どの仕組みがどのパスワードを使っているかが管理画面上で見えるようになり、不要になったものだけを個別に無効化できます。
ステップ5:失効を確認する
アプリケーションパスワードは、次のようなタイミングで無効になります。
- 管理画面から明示的に取り消した
- そのユーザーのログインパスワードを変更した
- ユーザー自体を削除・権限変更した
「昨日まで動いていたのに今朝から401」というパターンは、この失効が原因のことが多いです。 引き継ぎや退職でアカウント整理をした直後に自動投稿が止まったら、まずここを見ます。
通ったあとに必ず入れておくもの
401が解消して投稿できるようになったあと、運用で効いてくる仕掛けが2つあります。
1つ目は、更新後にREST APIで実際の反映を確認することです。 スクリプトの実行が割り込みなどで走らないまま「更新した」と扱われると、サーバー上は旧版のままになります。
GET /wp-json/wp/v2/posts/{ID}?context=edit
これを取得し、入れたはずの語が1つ以上あること、消したはずの語が0であることを数えて突き合わせます。 実行結果のログではなく、サーバー上のデータで判定するのが原則です。「実行した」と「反映された」は別の事実です。
2つ目は、401そのものを検知する仕組みです。 認証エラーで処理が空振りしても、スクリプト全体としては正常終了することがあります。この状態は、エラー通知が出ないまま何週間も気づかれません。実際に、内部のログイン処理が401で失敗し続け、データの同期が52日間止まっていた事例があります。止まっていたことに気づくまでの日数が損失そのものになります。
自動化に組み込むなら、次の3点を入れておきます。
- 認証エラーは通常のエラーと区別してログに残す
- 処理件数が0だった場合を「正常」として扱わない
- 実行ログではなく、投稿件数や更新日時などのデータ側で健全性を判定する
REST APIの本文を投稿するときの補足として、ブロックエディタを使っているサイトでは、段落や見出しをブロックの区切りコメント付きで投稿します。 生のHTMLだけを送ると、公開後のページは表示されるのに編集画面とプレビューが空に見えます。これは401とは別の問題ですが、「投稿できたのに完成していない」という状態になるため、あわせて押さえておくと手戻りが減ります。
まとめ
WordPressのアプリケーションパスワードで401になったときは、パスワードの再発行から入らないことが最短ルートです。
- 401は2種類ある。
rest_not_logged_inは認証が届いていない、rest_cannot_createは権限不足。エラーコードを見れば切り分けられる - 接続確認は投稿ではなく
users/me?context=editで行う。 認証と本文の問題を混ぜない - サイトにサーバー側のアクセス制限がかかっていれば、すべてのURLが401を返す。 WordPressの設定とは無関係
- サーバーがPHPをFastCGIで動かしている場合、Authorizationヘッダが落ちる。
.htaccessのWordPressブロック外に透過ルールを置く。内側に書くと自動更新で消える - 通ったあとは、反映の確認と401の検知をセットで入れる。 「実行した」は「反映された」ではない
同じ手順が別のサーバーで通らないのは、多くの場合サーバーの実行方式の差です。環境が変わったら、疎通確認からやり直す。 これを前提に組んでおけば、案件が増えても同じ場所で止まらなくなります。