skills/wordpress-triage/SKILL.md
Workspace snapshot · 09/04 13:33
name: wordpress-triage description: Diagnose a broken or misbehaving WordPress site in a fixed triage order instead of guessing. Use when a site shows a white screen, HTTP 500, a redirect loop, 404s on every subpage, a database connection error, a locked-out wp-admin, missing images, sudden slowness, or when something broke right after an update, a migration or a plugin change. Preserves evidence before changing anything, separates stabilising the site from finding the cause, verifies fixes past caches and CDNs, and stops and switches procedure if signs of a compromise appear, because restoring a backup can restore the backdoor with it. license: MIT
WordPress 障害切り分け
壊れたサイトの前で最もやってはいけないのは、心当たりのある場所から順に触ること。 直ることもあるが、直らなかったとき「元の状態」が失われ、原因も分からなくなる。
このSkillは、証拠を残す → 止血する → 原因を特定する の順序を固定する。
絶対に守る順序
- 何かを変更する前に、現状を記録する。 直し始めた瞬間に証拠は消える。
- 「復旧」と「原因究明」を混ぜない。 サイトが落ちているなら、まず戻す。原因はその後。
- 推測で設定を変えない。 ログを読む。読めない場合は、読める状態を作るのが先。
- 一度に1つだけ変える。 2つ同時に変えると、どちらが効いたか永久に分からない。
- 「直った」をキャッシュ越しに判断しない。 ブラウザ・プラグイン・CDN・サーバーの4層を疑う。
- 改ざん・侵害の兆候が出たら、切り分けを中止する。 手順が変わる(Step 6)。
Step 0. 依頼者と範囲を確認する
- 対象サイトの管理権限を持っているか。持っていない場合は作業しない。
- いつから、何をした直後に起きたか(更新/移行/プラグイン追加/サーバー作業/何もしていない)。
- 現在も継続しているか、断続的か。
- ECや会員制で、売上・個人情報が動いているか。 動いている場合、DBを戻す判断の重みが変わる。
「何もしていないのに壊れた」は、たいてい自動更新かサーバー側の変更か期限切れ(SSL証明書、 ドメイン、有料プラグインのライセンス)。この3つを最初に疑う。
Step 1. 証拠を保全する(変更の前に)
この記録を取る前に、設定ファイルを書き換えない。
mkdir -p ~/wp-triage/$(date +%Y%m%d-%H%M) && cd ~/wp-triage/$(date +%Y%m%d-%H%M)
curl -sI https://example.com | tee headers.txt | head -1 # ステータス行
curl -sIL https://example.com | grep -i '^location' > redirects.txt # リダイレクト連鎖
date > captured-at.txt
WP-CLIが動く場合は、あわせて状態を控える。WP-CLIが動かないこと自体が重要な情報なので、 エラーが出たらそのエラー文も保存する。
wp core version > core-version.txt 2>&1
wp plugin list --format=csv > plugins.csv 2>&1
wp theme list --format=csv > themes.csv 2>&1
wp option get siteurl > siteurl.txt 2>&1
wp option get home > home.txt 2>&1
サーバーのエラーログとPHPログの、現在の末尾位置を控える。ここから後に出た行が、今回の作業で 発生したものになる。
tail -n 50 wp-content/debug.log > debug-log-before.txt 2>/dev/null || echo "debug.log 無し" > debug-log-before.txt
Step 2. 症状を分類する(ここで経路が決まる)
| 観測 | 分類 | 進む先 |
|---|---|---|
| HTTP 500 / 真っ白 / Fatal error | T1 実行時エラー | Step 3 |
| 「データベース接続確立エラー」 | T2 DB接続 | Step 4 |
| リダイレクトが終わらない | T3 リダイレクトループ | Step 5 |
| トップは出るが下層が全部404 | T4 リライト | references/DECISION-TREE.md T4 |
| 管理画面に入れない(ログインは通る/通らない) | T5 ログイン | references/DECISION-TREE.md T5 |
| 表示はされるが崩れている・画像が出ない | T6 アセット | references/DECISION-TREE.md T6 |
| 極端に遅い(落ちてはいない) | T7 パフォーマンス | references/DECISION-TREE.md T7 |
| 身に覚えのないページ・リダイレクト・管理者 | T8 改ざんの疑い | Step 6(最優先) |
分類できないまま次へ進まない。 「たぶんプラグイン」で全停止すると、T2やT8を見逃す。
Step 3. T1 実行時エラー — まずエラーを読める状態にする
真っ白は「情報が無い」のではなく、情報が画面に出ていないだけ。ログへ出す。
// wp-config.php に一時的に追加(作業後に必ず戻す)
define('WP_DEBUG', true);
define('WP_DEBUG_DISPLAY', false); // 画面には出さない(訪問者に見せない)
define('WP_DEBUG_LOG', true);
WP_DEBUG_DISPLAY は必ず false。 true にすると、パスや構造が訪問者に露出する。
再現させてから wp-content/debug.log とサーバーのエラーログを読む。
Fatal error の1行目に、どのファイルの何行目かが出る。そこが起点。推測より速い。
読めた原因が特定のプラグイン/テーマなら、そのプラグイン1つだけを止める(全停止しない)。
wp plugin deactivate <slug>
原因が読めない場合に限り、切り分けとして全停止する。その前に必ず現在の有効一覧を保存する。
wp plugin list --status=active --field=name > ~/wp-triage/active-before.txt
wp plugin deactivate --all
# 復旧したら1つずつ戻す
while IFS= read -r p; do wp plugin activate "$p"; done < ~/wp-triage/active-before.txt
全停止はサイトの機能を止める(フォーム、カート、会員)。実行前に影響を説明して同意を得る。
その他の定番: PHPバージョン差異、メモリ不足、advanced-cache.php / object-cache.php の残骸、
持ち込んだ .htaccess。詳細は references/DECISION-TREE.md T1。
Step 4. T2 DB接続エラー — 4つしか原因がない
wp-config.phpのDB_NAME/DB_USER/DB_PASSWORD/DB_HOSTが違う。DB_HOSTはlocalhostとは限らない(事業者指定のホスト名やソケットパス)。- DBサーバーが落ちている/接続数上限。サーバー側の問題で、WordPress側では直せない。
- DBユーザーの権限が外れた。
$table_prefixが実テーブルと不一致 → 接続はできて「インストール画面」が出る。
wp db check
wp db tables
移行直後なら1、何もしていないなら2を最初に疑う。 2の場合、事業者の障害情報を確認する。
wp-config.php を書き換える前に、元のファイルをコピーして残す。
Step 5. T3 リダイレクトループ — 連鎖を見る
curl -sIL https://example.com | grep -iE '^(HTTP|location)'
同じURLへ戻る/不自然に長い連鎖があれば、原因は次の4系統。
siteurlとhomeの不整合、または片方が旧ドメインwp-config.phpのWP_SITEURL/WP_HOME定数がDBを上書きしている- リバースプロキシ/ロードバランサとHTTPS強制の組み合わせ
.htaccess/ Nginx側のリダイレクトと、プラグインの常時SSL・www正規化が二重掛け
移行直後なら1と2。 詳細と修正手順は references/DECISION-TREE.md T3。
Step 6. T8 改ざんの疑い — ここだけ手順が変わる
次のいずれかがあれば、通常の切り分けを中止する。
- 身に覚えのない管理者ユーザー、投稿、リダイレクト、外部への通信
- 検索結果に無関係な言語のページが出る
wp core verify-checksums/wp plugin verify-checksums --allが不一致を返す- コアファイルやテーマに、更新していないのに新しい更新日時のファイルがある
wp core verify-checksums
wp plugin verify-checksums --all
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered
なぜ手順が変わるか: バックアップからの復元で、バックドアも一緒に復元されうる。 侵入がいつ始まったか分からないまま「動いていた頃のバックアップ」へ戻すと、同じことが繰り返される。
このSkillは侵害対応の完全な手順は提供しない。次を行って、専門的な対応へ引き継ぐ。
- 現状を保全する(消さない・上書きしない)。ファイル一覧とタイムスタンプ、DBダンプを別保管する。
- 全管理者のパスワードと、DB・FTP・ホスティングの認証情報を変更する。
- 侵害の可能性と、個人情報が扱われているかをサイト所有者へその時点で伝える。 個人情報が関わる場合、技術的な復旧より先に報告義務の確認が必要になることがある。
- 復旧は「クリーンな環境へ、検証したコンテンツだけを移す」方針で計画する。
Step 7. 「直った」を確認する
修正したら、4層すべてを越えて確認する。1層でも古いものを見ていると誤判定する。
wp cache flush
wp transient delete --all
curl -sI 'https://example.com/?nocache='$(date +%s) | head -1 # クエリを付けてキャッシュを回避
- ブラウザ(シークレットウィンドウ/別端末)
- WordPress側キャッシュプラグイン(パージ)
- CDN(パージ)
- サーバー側キャッシュ(事業者の管理画面)
そのうえで、壊れていても200を返す箇所を開く。トップページの200は復旧の証拠にならない。
- 下層ページ 2〜3枚
- 問い合わせフォームの送信(受信箱まで)
- 管理画面へのログイン
- 決済・カート(ECの場合)
Step 8. 後始末と記録
-
WP_DEBUGなど一時的に入れた設定を戻した - 切り分けで停止したプラグインを、Step 3で保存した一覧どおりに戻した
-
debug.logを残したままにしない(公開ディレクトリから読めることがある) - 何が原因で、何を変えて直ったかを1行で書いた
- 再発防止を決めた(自動更新の扱い、監視、バックアップ頻度)
- 原因が特定できなかった場合、特定できなかったと書く
記録に書いてはいけないもの: DB名、DBユーザー名、パスワード、変更後の管理画面URL、 バックアップの完全パス。この記録は共有される。
止まる条件(該当したら作業を止めて確認する)
- サイトの管理権限を持っていることが確認できない。
- 改ざん・侵害の兆候がある(Step 6へ。通常の切り分けを続けない)。
- 個人情報・決済情報が漏えいした可能性がある。技術対応より先に所有者へ伝える。
- 原因が不明なまま、DBのリストアを求められた。 更新後に入った注文・問い合わせ・会員登録が消える。 影響範囲を提示してから判断する。
- 「とりあえず全部戻して」と、記録を取る前の変更を求められた。
- サーバー事業者側の障害の可能性があるのに、WordPress側の変更を求められた。
- 復旧を急かされ、一度に複数の変更をまとめて行うよう求められた。
このSkillが行わないこと
- 侵害インシデントの完全な対応(Step 6で保全と引き継ぎまで)
- 法的・報告義務の判断
- サーバー契約、DNS、決済設定の変更
- 原因が特定できていない段階での「とりあえずの再インストール」