
- はじめに
- 最初に確認した前提
- まずは小さくテストすることが重要
- 401 Unauthorized: Application Password認証が通らない
- Authorizationヘッダーが届いていない可能性
- パーマリンク設定を見直したら認証が通った
- 404 Not Found: /wp-json/形式でREST APIが見つからない
- ?rest_route=形式に対応させた
- 最小テスト投稿は成功した
- 403 Forbidden: 実記事だけ投稿できない
- 403は「認証失敗」とは限らない
- JSON送信からform送信に切り替えた
- WAF安全モードを追加した
- 本当の原因はPHPの形式だった
- ログを見て切り分ける
- Codexには診断より修正を任せた
- エラー対応で分かったこと
- 自動化では外部書き込み処理を慎重に扱う
- まとめ
はじめに
前回の記事では、品質チェック済みのAI生成記事を、WordPress REST APIで下書き登録する仕組みについて書いた。
筆者のブログ自動化ツールでは、記事構成案を作り、OpenAI APIで本文下書きを生成し、品質チェックを行い、問題なさそうなものだけWordPressへ下書き登録する流れにしている。
ここで使ったのが、WordPress REST APIである。
ただし、実装は一発では通らなかった。
実際には、次のようなエラーに順番に遭遇した。
401 Unauthorized
404 Not Found
403 Forbidden
それぞれ原因が違う。
しかも、同じ403でも「認証が通っていない403」と「本文がWAFに弾かれている403」では意味が違う。
本記事では、筆者がWordPress REST APIでAI生成記事を下書き投稿する際に詰まったエラーと、確認したポイントを整理する。
最初に確認した前提
今回やりたかったことは、WordPressへ記事を自動公開することではない。
目的は、AIで生成した本文をWordPressに下書きとして登録することである。
投稿ステータスは必ず draft とした。
status = draft
公開はWordPress管理画面で人間が確認してから行う。
また、WordPress REST APIの認証にはApplication Passwordを使った。
.env には、以下のような設定を入れる。
WORDPRESS_BASE_URL=https://syanaise3wariup.com
WORDPRESS_USERNAME=your_wordpress_username
WORDPRESS_APPLICATION_PASSWORD="your_application_password"
WORDPRESS_REST_API_MODE=rest_route
WORDPRESS_POST_BODY_MODE=form
WORDPRESS_WAF_SAFE_MODE=true
ここで使うパスワードは、WordPressの通常ログインパスワードではない。
WordPressのプロフィール画面で発行するApplication Passwordである。
この前提で、WordPress REST APIへ接続しようとしたところ、いくつかのエラーに遭遇した。
まずは小さくテストすることが重要
WordPress REST APIで投稿機能を作るとき、いきなり実記事を投稿しない方がよい。
筆者は、以下の順番で切り分けることにした。
1. REST APIのエンドポイントが見えるか
2. Application Password認証が通るか
3. 最小のテスト投稿ができるか
4. 実際の記事本文を投稿できるか
この順番が重要である。
いきなり実記事を送ってエラーになると、原因が分からない。
認証が悪いのか、REST APIのURLが違うのか、本文がWAFに弾かれているのか、切り分けが難しくなる。
そのため、最初は短い本文でテスト投稿する。
タイトル: API投稿テスト
本文: これはREST API投稿テストです。
ステータス: draft
これが通るかどうかで、その後の判断が変わる。
401 Unauthorized: Application Password認証が通らない
最初に詰まったのが、401エラーである。
/wp/v2/users/me にアクセスして認証確認をしたところ、401が返ってきた。
401は、ざっくり言えば「ログイン済みユーザーとして認識されていない」という状態である。
このとき確認したのは以下である。
- WordPressのユーザー名が正しいか
- 表示名ではなくログイン用ユーザー名を使っているか
- 通常のログインパスワードではなくApplication Passwordを使っているか
- Application Passwordの空白や改行が混ざっていないか
- Application Passwordを作成したユーザーと、
.envのユーザー名が一致しているか
Application Passwordは、発行時に空白区切りで表示されることがある。
たとえば、以下のような形式である。
abcd efgh ijkl mnop qrst uvwx
この空白を含めるべきか、除去するべきかで迷うことがある。
筆者の環境では、パスワードそのものを確認し直し、必要に応じて再発行した。
ただ、それでも認証が通らない場合がある。
Authorizationヘッダーが届いていない可能性
ユーザー名もApplication Passwordも合っているのに401になる場合、AuthorizationヘッダーがWordPressまで届いていない可能性がある。
Pythonの requests でBasic認証を使うと、Authorizationヘッダーが送られる。
しかし、サーバー構成やWordPressの設定によっては、そのヘッダーがPHPやWordPress側へ渡らないことがある。
この場合、WordPressから見ると「認証情報が送られていない」ように見える。
筆者もこの可能性を疑った。
確認したポイントは以下である。
- WordPressのサイトヘルスにAuthorization headerの警告が出ていないか
.htaccessでAuthorizationヘッダーを渡す設定が必要ではないか- セキュリティプラグインがREST API認証を止めていないか
- サーバー側WAFやセキュリティ設定がBasic認証を遮断していないか
ただし、今回の環境では最終的に、パーマリンク設定を変更・保存することで認証が通るようになった。
パーマリンク設定を見直したら認証が通った
今回、認証まわりで最終的に効いたのが、WordPressのパーマリンク設定である。
WordPress管理画面で、以下を確認した。
設定
↓
パーマリンク
この設定を見直し、保存し直すことで、REST APIの認証が通るようになった。
パーマリンク設定は、REST APIのルーティングにも関係することがある。
そのため、Application Password認証が通らない場合でも、単純にパスワードだけを疑うのではなく、パーマリンク設定やREST APIのルーティングも確認した方がよい。
WordPress REST APIのエラーは、見た目より奥が深い。
401だからといって、必ずしもパスワード間違いとは限らない。
404 Not Found: /wp-json/形式でREST APIが見つからない
認証が通るようになったあと、次に遭遇したのが404である。
一般的にWordPress REST APIは、次のようなURLでアクセスする。
https://example.com/wp-json/wp/v2/posts
しかし、筆者の環境では、この /wp-json/ 形式で404が返ることがあった。
このとき返ってきたのは、WordPress REST APIのJSONエラーではなく、HTMLの404ページである。
これは重要な違いである。
REST APIのJSONエラーならWordPress側の応答である可能性が高い。
一方、HTMLの404ページなら、WordPress REST APIのルートとして処理されていない可能性がある。
そこで、?rest_route= 形式を試した。
https://syanaise3wariup.com/?rest_route=/wp/v2/posts
この形式では通るようになった。
?rest_route=形式に対応させた
今回のツールでは、REST APIのURL形式を設定で切り替えられるようにした。
.env には以下を設定する。
WORDPRESS_REST_API_MODE=rest_route
対応するモードは以下である。
wp_json
rest_route
auto
wp_json の場合は、以下の形式を使う。
https://example.com/wp-json/wp/v2/posts
rest_route の場合は、以下の形式を使う。
https://example.com/?rest_route=/wp/v2/posts
auto の場合は、どちらが使えるか自動判定する。
今回の環境では、rest_route 形式を使うことで投稿できるようになった。
WordPress REST APIを使う場合、/wp-json/ 形式だけを前提にすると詰まることがある。
特にレンタルサーバーやパーマリンク設定の影響を受ける環境では、?rest_route= 形式も試す価値がある。
最小テスト投稿は成功した
認証とREST APIのURL形式を修正したあと、まず短い本文でテスト投稿した。
テスト内容は以下である。
タイトル: API投稿テスト
本文: これはREST API投稿テストです。
ステータス: draft
この最小投稿は成功した。
ここで分かったことは以下である。
Application Password認証は通っている
REST APIのURL形式も正しい
WordPressへdraft投稿できる
ここまで通れば、基本的なREST API接続は成功である。
しかし、実際の記事下書きを投稿すると、また別の問題が出た。
403 Forbidden: 実記事だけ投稿できない
最小テスト投稿は成功した。
しかし、実際のAI生成記事を投稿すると403になった。
ここがややこしいところである。
認証は通っている。
REST APIのURLも正しい。
短い本文なら投稿できる。
それなのに、実記事だけ403になる。
この場合、疑うべきは本文の中身である。
技術記事には、次のようなものが含まれやすい。
- PowerShellコマンド
- Pythonコード
- Windowsパス
- JSON
- YAML
- HTMLタグ
- URL
- Markdownのコードブロック
これらが、サーバー側のWAFに攻撃っぽく見えることがある。
今回の403も、WordPress REST APIのJSONエラーではなく、HTMLの403ページだった。
つまり、WordPress本体というより、サーバー側のWAFやセキュリティ機能に弾かれている可能性が高いと考えた。
403は「認証失敗」とは限らない
403というと、権限不足を連想しやすい。
しかし、今回のように実記事だけ403になる場合は、認証や権限とは別の可能性がある。
切り分けとしては、以下を見るとよい。
/users/me は成功するか
最小テスト投稿は成功するか
短い本文なら投稿できるか
実記事だけ失敗するか
レスポンスはJSONかHTMLか
今回のように、認証確認も最小投稿も成功しているなら、REST APIそのものは動いている。
そのうえで実記事だけ403なら、本文内容がWAFに引っかかっている可能性が高い。
これは、コードだけではなくサーバー側の挙動も関係する。
JSON送信からform送信に切り替えた
WAF対策として、まず投稿時の送信方式を切り替えられるようにした。
最初はJSONで送っていた。
requests.post(url, auth=(user, password), json=payload)
これを、フォーム送信に切り替えられるようにした。
requests.post(url, auth=(user, password), data=payload)
.env には以下を設定した。
WORDPRESS_POST_BODY_MODE=form
環境によっては、JSON本文の方がWAFに強く見られることがある。
form送信に切り替えることで通る場合があるため、設定で変えられるようにした。
こうした回避策は、環境依存である。
そのため、コードに固定するより、.env で切り替えられるようにした方が扱いやすい。
WAF安全モードを追加した
それでも実記事が弾かれる場合に備えて、WAF安全モードも追加した。
WORDPRESS_WAF_SAFE_MODE=true
WAF安全モードでは、WordPressへ送る本文だけを安全化する。
主な処理は以下である。
- コードブロックをプレースホルダー化する
- 長いコマンド列をそのまま送らない
- Windowsパスや記号の多い行を抑える
- 危険視されやすいHTMLタグを除去する
- 本文末尾に、詳細コマンドは管理画面で確認・追記する旨を入れる
重要なのは、元のMarkdownファイルは変更しないことである。
あくまでWordPressへ送るpayloadだけを安全化する。
これにより、記事本文がWAFに弾かれるリスクを下げられる。
ただし、WAF安全モードを使った場合、WordPress管理画面で本文を確認し、必要に応じてコードブロックを手動で整える必要がある。
完全自動ではなく、安全に下書き登録するための妥協点である。
本当の原因はPHPの形式だった
WAFの安全モード云々言いながら、実はこれでは403は解消しなかった。
これは何度chatGPTやGeminiに聞いても分からなくて、結局は昔ながらの検索で調べたのだが、1件だけ同じようなケースに言及しているサイトがあった。
そこで紹介されていたのが、レンタルサーバのPHPがCGI版だとエラーになるというものだった。
試しに確認してみると確かにこのサイトはCGI版で動いていた。
これをモジュール版にすれば良いとのことなので変更してみると、想定通り403のエラーが出なくなった。
AIに聞いて何でも分かるような気がしていたが、レンタルサーバの設定という視点はなく、検索して初めて気づいた観点であった。
ログを見て切り分ける
今回のエラー対応では、ログがかなり役に立った。
投稿処理では、以下をログに出すようにした。
- 実行モード
- 対象候補数
- REST APIのURL
- 認証テストの成否
- 投稿対象ファイル
- エラーステータス
- レスポンス先頭部分
- 結果CSVの保存先
たとえば、以下のような情報があると、状況を把握しやすい。
requested_mode=rest_route
resolved_mode=rest_route
users_me_url=https://example.com/?rest_route=/wp/v2/users/me
posts_url=https://example.com/?rest_route=/wp/v2/posts
WordPress auth test succeeded
WordPress API error 403
このログから、認証とREST APIのURLは成功しているが、投稿本文で403になっていると判断できる。
自動化ツールでは、エラーが出ること自体よりも、どこで失敗したか分からないことの方がつらい。
ログは、暗い廊下に置く小さなランタンである。
Codexには診断より修正を任せた
今回、REST APIの接続診断は人間側で行った。
Codexには、どちらかというと修正や機能追加を任せた。
たとえば、以下のような対応である。
REST API URL形式を切り替えられるようにする
post body modeをjson/formで切り替えられるようにする
WAF安全モードを追加する
403時のエラーメッセージを分かりやすくする
投稿結果をCSVとMarkdownレポートに残す
REST APIの接続確認は、実際のサーバー環境やWordPress設定を見ながら切り分ける必要がある。
そのため、Codexには「このエラーの原因を完全に診断して」と頼むより、こちらで切り分けた結果をもとに、必要な修正を実装させる方が進めやすかった。
Codexは、現場のサーバー設定そのものは見られない。
しかし、こちらが原因の方向性を伝えれば、設定切り替えや例外処理の実装はかなり役に立つ。
エラー対応で分かったこと
今回のWordPress REST API連携で分かったことは、エラーの数字だけ見ても判断できないということである。
同じ401でも、原因は単純なパスワードミスとは限らない。
同じ404でも、記事が存在しないのではなく、REST APIのルーティングが効いていない可能性がある。
同じ403でも、権限不足ではなく、WAFが本文を弾いている可能性がある。
重要なのは、段階的に確認することである。
GETでREST APIが見えるか
/users/meで認証が通るか
最小POSTが通るか
実記事POSTが通るか
この順番で確認すれば、原因をかなり絞り込める。
自動化では外部書き込み処理を慎重に扱う
今回のWordPress下書き投稿は、外部サービスへ書き込む処理である。
このような処理は、慎重に扱う必要がある。
筆者は以下を徹底した。
dry-runを用意する
executeを明示した時だけ投稿する
status=draftに固定する
最初は1件だけ投稿する
投稿結果をログに残す
エラー時はHTMLレスポンスの先頭を記録する
特に重要なのは、status=draft である。
エラー対応中に自動公開まで行っていたら、かなり危険だった。
下書き投稿だけにしていたからこそ、安心して試せた。
AI生成記事をWordPressに送る場合、最初に作るべきなのは公開機能ではない。
安全な下書き登録機能である。
まとめ
筆者は、WordPress REST APIでAI生成記事を下書き投稿する仕組みを作る中で、401、404、403のエラーに遭遇した。
401では、Application Password認証、ユーザー名、Authorizationヘッダー、パーマリンク設定を確認した。
404では、/wp-json/ 形式だけでなく、?rest_route= 形式を試した。
403では、認証や最小投稿が通っているかを確認し、実記事だけ失敗する場合はWAFによる本文ブロックを疑った。
最終的には、WORDPRESS_REST_API_MODE=rest_route、WORDPRESS_POST_BODY_MODE=form、WORDPRESS_WAF_SAFE_MODE=true などの設定を用意し、環境に合わせて切り替えられるようにした。
WordPress REST API連携は、コードだけで完結しない。
WordPress設定、パーマリンク、サーバーWAF、セキュリティプラグインなども関係する。
だからこそ、段階的な切り分けとログ出力が重要である。
AI生成記事をWordPressへ送る場合でも、自動公開はせず、まずは安全に下書き登録する。
この設計にしておいたことで、エラー対応もしやすくなった。
次回は、Python自動化ツールで .env と .env.example を分ける理由や、GitHubに上げてはいけないファイルについて整理する予定である。


コメント