← CLAUDE-PENGIN-TOOLS
DOCUMENT · 14.9 KB

skills/wordpress-migration/references/TROUBLESHOOTING.md

Workspace snapshot · 09/04 13:33

WordPress 移行 トラブルシューティング

症状 → 原因 → 対処。SKILL.md と RUNBOOK.md の手動確認結果から引く。

まず確認する共通の3点:

wp option get siteurl ; wp option get home     # 期待値と一致するか
wp config get WP_SITEURL --type=constant       # 定数がDBを上書きしていないか
wp config get WP_HOME    --type=constant
wp plugin list --status=active                 # キャッシュ/セキュリティ系が動いていないか

S1. 真っ白(白画面) / HTTP 500

原因の切り分け順:

  1. PHPの致命的エラー。wp-config.php に一時的に次を入れてログへ出す。
    define('WP_DEBUG', true);
    define('WP_DEBUG_DISPLAY', false);
    define('WP_DEBUG_LOG', true);
    
    wp-content/debug.log とサーバーのエラーログを読む。推測で直さない。
  2. PHPバージョン差異。移行先のPHPが新しく、テーマ/プラグインが非対応。 → 移行先のPHPを移行元と同じバージョンへ下げて復旧させ、PHP更新は移行完了後の別作業にする。
  3. .htaccess の持ち込み。Apache→Nginx、または事業者固有ディレクティブが不正。 → .htaccess を退避し、wp rewrite flush --hard で標準の内容を再生成する。
  4. メモリ不足。define('WP_MEMORY_LIMIT', '256M'); と php.ini の memory_limit を確認。
  5. advanced-cache.php / object-cache.php の残骸。キャッシュプラグインを外したのにドロップインが残っている。 → 両ファイルを退避する。
  6. プラグイン起因の切り分け:
    wp plugin list --status=active --field=name > ~/active-plugins-before-debug.txt
    wp plugin deactivate --all
    # 復旧したら1つずつ有効化して原因を特定する
    while IFS= read -r plugin; do wp plugin activate "$plugin"; done < ~/active-plugins-before-debug.txt
    
    全停止は影響を説明して承認を得てから行い、復旧後は記録した一覧だけを戻す。

S2. 「データベース接続確立エラー」

  • wp-config.php の DB_NAME / DB_USER / DB_PASSWORD / DB_HOST が移行先の値になっていない。 DB_HOST は localhost でないことが多い(事業者指定のホスト名やソケットパス)。
  • DBユーザーに権限が付与されていない。
  • $table_prefix が実際のテーブルと違う → 接続はできるが「インストール画面」が出る。
  • 確認:
    wp db check
    wp db tables
    

S3. リダイレクトループになる(無限にリダイレクトしました)

移行事故の代表格。原因は4系統。

  1. siteurl と home の不整合、または片方が旧ドメイン。
    wp option get siteurl ; wp option get home
    wp option update siteurl 'https://new.example.jp'
    wp option update home    'https://new.example.jp'
    
  2. wp-config.php の定数がDBを上書きしている。WP_SITEURL / WP_HOME が旧値のまま。 → 定数を新URLへ更新するか削除する。
  3. FORCE_SSL_ADMIN とロードバランサ/リバースプロキシの組み合わせ。 プロキシがバックエンドへHTTPで渡しているのにWordPress側がHTTPSを強制し、永久に往復する。
    if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
        $_SERVER['HTTPS'] = 'on';
    }
    
    (wp-config.php の require_once ABSPATH . 'wp-settings.php'; より前に置く)
  4. .htaccess / Nginx側のリダイレクトと、プラグインの常時SSL・www正規化が二重に効いている。 → どちらか片方に統一する。curl -sIL https://new.example.jp | grep -i '^location' で連鎖を見る。

curl -sIL の Location 列を読み、同一URLへの回帰や不自然に長い連鎖があれば切替を止める。


S4. トップは出るが下層ページが全部 404

  • パーマリンク設定の未反映。
    wp rewrite structure '/%postname%/' --hard
    wp rewrite flush --hard
    
    管理画面の「設定 > パーマリンク」を開くだけでも再生成される。
  • .htaccess が無い / 書き込めない(Apache)。WordPress標準ブロックを手で戻す。
  • AllowOverride None(Apache)で .htaccess が読まれていない → サーバー設定側で許可する。
  • Nginx に try_files ... /index.php?$args; が無い → サーバー設定を修正する。
  • 移行元がサブディレクトリ設置で、移行先がドキュメントルート直下になった(またはその逆)。 → siteurl / home のパス部分を実際の設置場所に合わせる。

S5. ウィジェット・カスタマイザー・プラグイン設定・ページビルダーのレイアウトが消えた

シリアライズデータの破損。 原因はほぼ一つで、sed や SQL の REPLACE() でURLを置換したこと。

s:29:"https://old.example.com/about"    ← 宣言長 29
s:29:"https://new-site.example.jp/about" ← 実長は違う → unserialize() が false を返す
  • 復旧方法は置換前のバックアップからのリストアのみ。 壊れた値を手で直すのは現実的でない。
    wp db import ~/pre-replace-XXXXXXXXXXXX.sql
    
  • リストア後、必ず SKILL.md Step 5 の6形式を、wp search-replace --precise の dry-runからやり直す。
  • 症状が出るのは置換の数時間後であることが多い(キャッシュが切れてから)。 置換直後の目視確認では気づけないため、置換後は必ずウィジェット・カスタマイザー・ ページビルダーの編集画面を開いて中身を確認する。
  • Elementor / Divi など、JSONを入れ子で保存するビルダーは エスケープ形(https:\/\/old.example.com)の置換漏れでも壊れる。 SKILL.md Step 5 はこの形を別パスとして処理する。

S6. 画像が表示されない/URL が旧ドメインのまま

  1. DB内の残存を数える。
    wp db search 'old.example.com' --all-tables-with-prefix --stats
    
    0件でなければ置換漏れ。エスケープ形・プロトコル相対形も含めて再置換する。
  2. uploads が転送されていない。ファイル数とサイズを両側で照合する。
    find wp-content/uploads -type f | wc -l ; du -sh wp-content/uploads
    
  3. ファイルはあるが 403。パーミッション・所有者がWeb実行ユーザーと合っていない。
  4. サムネイルだけ出ない。中間サイズが未生成。
    wp media regenerate --yes
    
  5. CDN / オフロードプラグインが旧ドメインを指している。プラグイン設定側のURLを更新する。
  6. wp-config.php の WP_CONTENT_URL / UPLOADS が旧値のまま。

S7. 混在コンテンツで鍵マークが出ない

  • HTML内に http:// の絶対URLが残っている。
    wp search-replace 'http://new.example.jp' 'https://new.example.jp' \
      --all-tables-with-prefix --precise --report-changed-only --skip-columns=guid --dry-run
    
    (同一ホストのスキーム引き上げ。--old にスキームを明示すること)
  • 外部リソース(他サイトの画像、古い埋め込み、フォント、アナリティクス)が http://。 → 自ドメインの置換では直らない。該当箇所を個別に修正する。
  • CSS/JS内のハードコードURL、テーマの設定値、wp_options 内のシリアライズ値。
  • 対症療法としてのアップグレード指示(Content-Security-Policy: upgrade-insecure-requests)は 原因を隠すだけ。まず実体を直す。

S8. 移行後に検索から消えた/noindex が残っている

ステージング本番化で最も損失が大きく、最も気づかれにくい事故。

wp option get blog_public        # 1 が公開。0 なら検索避けが有効
wp option update blog_public 1
  • HTMLの <meta name="robots" content="noindex">、または X-Robots-Tag HTTPヘッダー。 ヘッダー側はブラウザで見えないので気づきにくい。
    curl -sI https://new.example.jp | grep -i x-robots-tag
    
  • SEOプラグイン(Yoast / Rank Math / SEO SIMPLE PACK)側の noindex 設定が別に存在する。 サイト全体設定と、投稿タイプごとの設定の両方を見る。
  • サーバー/事業者のステージング機能が自動でヘッダーを付けている場合がある(機能をオフにする)。
  • Basic認証が残っていると検索エンジンはクロールできない。curl -sI が 401 を返さないか確認。
  • 修正後、Search Console でサイトマップを再送信。ドメイン変更ならアドレス変更ツールを使う。
  • 旧ドメイン→新ドメインの301は最低1年維持する。評価の移転はリダイレクトが前提。

S9. canonical が旧ドメインを指している

  • DB内の置換漏れ(S6の手順1)。
  • SEOプラグインに canonical のベースURLがハードコードされている。
  • キャッシュ済みHTMLが配信されている。
    wp cache flush ; wp transient delete --all
    
    加えてCDN側のキャッシュもパージする。
  • 放置すると新ドメインがインデックスされない。公開判定では致命的な未完了として扱う。

S10. robots.txt に Disallow: / が残っている

  • 物理ファイルが存在する場合、WordPressの仮想 robots.txt は返らない。 ルート直下の robots.txt を確認し、ステージング用の内容なら削除または修正する。
  • 物理ファイルが無く blog_public が 0 なら、WordPress が自動で Disallow: / を返す(S8)。
  • robots.txt が 404 の場合、パーマリンク再生成(S4)で仮想robots.txtが復活することがある。
  • robots.txt によるブロックは noindex とは別問題。両方確認する。

S11. <title> が空 / canonical が無い

  • テーマが正しく読み込まれていない(テーマファイル未転送、テーマ名ディレクトリの大文字小文字違い)。 Linux間でも移行元がmacOS/Windows経由だと大小が変わることがある。
    wp theme list
    
  • SEOプラグインが停止している。
  • ページが実際にはエラーページを返している(S1と併発)。
  • 単独なら軽微な場合もあるが、HTTPエラーなどと同時に出ている場合は テーマ/プラグインの読み込み失敗を疑う。

S12. 到達不能/DNS がまだ旧サーバーを指している

dig +short new.example.jp
dig +short @8.8.8.8 new.example.jp
dig +short @1.1.1.1 new.example.jp
  • TTLが経過していない。下げていなかった場合は最大で旧TTL(例: 3600秒〜86400秒)待つ。
  • 自端末やルーターのDNSキャッシュ。ネームサーバーを直接指定して確認する。
  • ネームサーバー自体が旧事業者のまま(Aレコードを変えても、権威が別ならそちらが引かれる)。
  • CAA レコードで証明書発行が拒否されている(SSLが張れない場合)。
  • 切替前の検証は hosts ファイルで行う(RUNBOOK Phase 9)。DNSの浸透を待たずに検証できる。

S13. フォームからメールが届かない

移行で壊れやすいが、移行後の確認から漏れやすい組み合わせ。

  • 送信元ドメインのSPF / DKIM / DMARC が旧サーバーのIPを許可している。 → 新サーバーのIPまたは送信サービスを許可するようDNSを更新する。
  • 事業者が mail() を許可していない、またはSMTP必須。→ SMTPプラグインで明示設定する。
  • From アドレスが自ドメインでないため受信側で拒否される。
  • 確認:
    wp eval 'var_dump(wp_mail("test@example.com","migration test","body"));'
    
    true でも受信側で捨てられることがあるため、実際の受信箱で確認する。

S14. 予約投稿・定期処理が止まった

  • DISABLE_WP_CRON が定義されていて、外部cronが旧ドメインのURLを叩いている。 → 外部cronの設定を新URLへ更新する。
  • 溜まったイベントを確認する。
    wp cron event list
    wp cron test
    
  • サーバーのcronジョブ自体が移行されていない(サーバー設定は移行対象に入らない)。

S15. ログインできない / ログインしても管理画面へ入れない

  • 認証ソルトキーを再生成したため既存セッションが無効になった(正常。再ログインで解決)。
  • COOKIE_DOMAIN が旧ドメインのまま定義されている → 更新または削除する。
  • ログイン後にループする場合はS3の系統。
  • パスワードをリセットする。値をコマンドライン引数へ直接書かず、WP-CLIの対話入力を使う:
    wp user update <login> --prompt=user_pass
    
  • セキュリティプラグインのIP制限・国別制限が新環境で誤作動している → プラグインディレクトリ名を一時的に変更して無効化する。

S16. 「置換したのに変わらない」

原因は次のいずれか。上から順に確認する。

  1. wp-config.php の WP_SITEURL / WP_HOME 定数がDBの値を上書きしている(最頻)。
  2. ページキャッシュ(プラグイン / サーバー / CDN / ブラウザ)が古いHTMLを返している。
  3. 置換対象のDBが違う(複数DBがある、ステージングを見ている)。
  4. --old の指定が実際の格納形式と一致していない(www. の有無、ポート付き、エスケープ形)。
    wp db search 'old.example' --all-tables-with-prefix --stats
    
  5. 接頭辞外のテーブルに格納されている(search-replace は --all-tables-with-prefix の範囲外を触らない)。 wp db tables --all-tables と wp db tables --all-tables-with-prefix の差分を確認する。

判断に迷ったときの原則

  • 推測で本番を触らない。 症状を再現できるか、ログに根拠があるかを先に確認する。
  • 1回に1つだけ変える。 複数同時に変えると、直った理由も壊れた理由も分からなくなる。
  • 復旧の見込みが立たない場合、時間を使い切る前にロールバック(RUNBOOK Phase 13)へ切り替える。 判断のデッドラインは作業開始前に利用者と合意しておく。
  • WARN を「たぶん平気」で飛ばさない。移行の事故は WARN の積み残しから起きる。

MIT License. Provided as-is, without warranty. Part of PENGIN Tools.