「フロントは完璧なのに、管理画面だけ文字が消える」— WordPress移行後に遭遇した謎バグの正体
WordPress移行後に遭遇した謎バグのきっかけ
ローカル環境でWordPressテーマを一通り作り込み、All in One WP Migrationを使ってテスト環境(Xserver)へ移行した。
フロントエンドの表示を確認して「よし!」と一安心したのも束の間で、管理画面の投稿編集画面(ブロックエディタ)を開いたところで手が止まりました。
投稿編集画面の本文のテキストがほぼ全部、白文字になっていて読めません。
マーカーで色をつけた部分だけは、かろうじて見えますが、それ以外の通常の文章は背景と同化して消えていました。
原因の切り分けに1時間以上かかったのですが、最終的にたどり着いたのはadd_editor_style()に渡すパスを、フルURLからテーマ相対パスに変更しただけという環境依存の見えにくい落とし穴でした。
同じ症状で悩む人の参考になればと思い、調査の過程を記録しておきたいと思います。
やろうとしたこと
もともとの目的は、ローカルで作り込んだWordPressテーマを、本番公開前のテスト環境(Xserver)に一度きちんと移行して、クライアントに見てもらえる状態に仕上げることでした。
デザインの実装はほぼ終わっていて、残っていたのは投稿ページやテンプレートの最終確認と、実際にコンテンツを入れながら管理画面側の使い勝手をチェックする作業。
もともと管理画面のブロックエディタで、テキストの色や背景色を標準の色パネルから変更しても意図通りに反映されないという問題がありました。
これを解消するため、テーマのCSSをadd_editor_style()でエディタ側にも読み込ませる実装を追加していました。
All in One WP Migrationでテスト環境に移行し、フロント表示に問題がないことを確認したら、そのままクライアントへの共有URLとして渡す予定でした。
つまりこの時点では「移行作業はほぼ完了、あとは最終チェックだけ」という認識で、まさか管理画面の投稿編集画面でこんな症状に引っかかるとは思っていませんでした。
症状を整理する
- フロントエンドの表示は問題なし
- 管理画面の投稿編集画面だけ、通常のテキストが白文字で読めない
- ローカル環境では再現せず、テスト環境でのみ発生
「移行先の環境だけで起きる」という時点で、何かしら環境固有の要因が絡んでいそうだと思い、調査を始めました。
管理画面バグでまず疑ったこと、外れた仮説
仮説1:投稿の中身に白色が指定されている
単純に投稿画面でテスト時にテキストに白色を指定していたかもしれません。
実際に確認してみると、該当ブロックで確かにテキスト色が白系になっていました。
単純に「こんな凡ミスか。。」と思ったのも束の間で、他の投稿を開いてテキストに色を指定していない箇所も同じ症状が出ることが判明。
個別の投稿データの問題ではありませんでした。
仮説2:エディタにテーマのCSSが読み込まれていない
そもそもローカル環境で開発していた時点で、管理画面の色パネルからテキスト色や背景色を変更しても、意図通りに反映されないという問題がありました。
これを解消するために、add_editor_style()でテーマのCSSを管理画面のエディタ側にも読み込ませる実装を追加していて、その中に文字色を指定するCSSも含めていました。
WordPressのブロックエディタは`.editor-styles-wrapper`というクラスでコンテンツを囲む仕組みになっているので、ここに文字色を指定するCSSを追加していました。
.editor-styles-wrapper,
.editor-styles-wrapper p,
.editor-styles-wrapper h1,
.editor-styles-wrapper h2,
.editor-styles-wrapper h3,
.editor-styles-wrapper h4 {
color: var(--color-text);
}このCSSのおかげで、ローカル環境では色パネルの問題は解消。
つまりローカルでは元々別の症状(色指定が反映されない)があり、その対処としてこのCSSをすでに書いていたのであって、今回の白文字バグとは発生のタイミングも原因も別でした。
その状態のままテスト環境(Xserver)に移行したところ、今度は「テキストが白文字で読めない」という新しい症状が発生しました。
同じCSSはすでに書かれているはずなのに、なぜかテスト環境では効いていません。
この時点で「テーマのCSSがそもそもエディタに読み込まれていないのでは」という仮説を立てて、もっと別の角度から調べることにしました。
環境まわりを一つずつチェック
思いつく限りの要因を、AIと壁打ちしながら機械的に一つずつ探しました。
WP_DEBUGはfalseで確認済みなので開発モードの誤動作ではない。- WordPress本体のバージョンもPHPのバージョンもローカルと揃えてみたが直らない。
- キャッシュ系プラグインはそもそも導入していない。
- Xserverの高速化機能(XPageSpeed設定)もすべてOFFにして確認した。
- セキュリティプラグインとACFも無効化してみたが変化なし。
object-cache.phpやadvanced-cache.phpのような永続キャッシュ用ドロップインも存在しない。
プラグイン、バージョン差、キャッシュまわり、一通り候補から外れました。ここまで来ると、さすがに腰を据えて調べる必要が出てきました。
PHPが実際に何をしているか、ログで見る
AIに頼んで一時的にerror_log()を仕込んでもらい、CSSを読み込ませる処理(add_editor_style())が実際に呼ばれているかをサーバーのエラーログで確認しました。
[enqueue_editor_styles] environment=manifest
[get_build_manifest] ... has_style_entry=yes
[enqueue_editor_style_from_manifest] add_editor_style(https://example.com/.../style.XXXXXXXX.css)PHP側は完璧に、正しいURLでadd_editor_style()を呼び出せていました。ファイルの存在もmanifestの中身も問題ありません。「じゃあ何が悪いんだ」と、沼に入りはじめました。
原因
add_editor_style()に渡すパスの形式
add_editor_style()は、渡すパスの形式によって挙動がまったく変わります。
テーマ相対パス(assets/styles/style.css)を渡した場合は、WordPressがサーバー上のファイルを直接読み込みます。
一方でフルURL(https://example.com/.../style.css)を渡した場合、ブロックエディタはiframeの中にCSSを埋め込む必要があるため、サーバーが自分自身にHTTPリクエストを送って中身を取得しにいく「ループバック接続」というやつが発生します。
今回の作業では、フルURLを渡していました。
クラシックエディタは渡されたURLをブラウザにそのまま渡すだけなのでこの仕組みの影響を受けませんが、ブロックエディタはサーバー側でそのURLに自分自身でアクセスして中身を取得する必要があります。
そして今回のテスト環境(Xserver)では、このループバック接続がファイアウォールかセキュリティ設定か何かの理由でブロックされていました。
しかもこの失敗はエラーも出さずに静かに起きてしまいます。
フロントは完璧、PHPのログも正常、クラシックエディタは無事、ブロックエディタだけ死んでいる。この一見矛盾した状況は、すべてこのループバック接続の失敗が原因でした。
ループバックとは
サーバーが自分自身に電話をかけるような通信のことです。
普段は「お客さん(ブラウザ)がお店(サーバー)に電話をかける」という一方向のやり取りですが、ループバックは「お店が自分のお店に電話をかける」という、ちょっと変わった通信です。
この自己通信も、外部からの電話と同じ回線を通るため、回線側のセキュリティ設定によっては「怪しい」と判断されて、こっそり弾かれてしまうことがあります。
対処法
add_editor_style()に渡すパスを、フルURLからテーマ相対パスに変更しました。
// Before
$style_path = get_template_directory_uri() . "/" . $style_entry["file"];
add_editor_style($style_path);// After
add_editor_style($style_entry["file"]);これだけで即座に解決しました。
1時間以上かけて追いかけた原因が、パスの書き方ひとつだったというのは拍子抜けですが、長く悩んだ後の原因なんていつもそんなものです。
なぜ通常のCSSは読み込めて、add_editor_style()だけ読み込めなかったのか
原因が分かって一件落着、と思ったところで、ここでちょっとした疑問が浮かびました。
CSSを読みに行けていなかったのが原因なら、サイト全体のスタイルも当たらなくなってもおかしくないはずなのに、なぜフロント側は完璧に表示されていたのか?
調べてみると、フロント側とエディタ側でCSSの読み込み方の仕組みがまったく違うことが理由でした。
通常のサイト表示はwp_enqueue_style()でCSSを登録します。
これがやっているのは、HTMLの中に
<link rel="stylesheet" href="https://example.com/style.css">というタグを1つ追加しているだけ。
このタグが実際にCSSを取得しに行くのはブラウザの仕事で、訪問者のブラウザが自分自身でこのURLにアクセスしてダウンロードします。
サーバーは「このURLですよ」と教えているだけで、サーバー自身が何かを取得しに行く必要は一切ないので、ループバック接続は関係なく普通に動きます。
一方でブロックエディタの編集画面は、iframe(ページの中に埋め込まれた、もう1つの小さいページ)として作られています。
このiframeの中にCSSを反映させるには、リンクタグを貼るだけでは済まず、WordPressはCSSファイルの中身そのものをテキストとして取得して、styleタグの中に直接埋め込むという処理をします。
このときadd_editor_style()に渡したのがフルURLだと、「これはひょっとして外部のCSSかもしれない」という扱いになり、サーバーがそのURLにHTTPリクエストを送って中身を取得しようとし、サーバーが自分自身にアクセスするいわゆるループバック接続が発生します。
サイト全体のスタイルはフロント側の仕組み(ブラウザが直接取得するだけ)を使っていたので、この危険な自己通信を一切経由していませんでした。
ブロックエディタだけがこの特殊な取得方法を必要としていたので、そこだけがピンポイントで壊れていた、という構造でした。
なぜ気づきにくかったのか
- フロント側の表示にはまったく影響せず、エディタ画面限定の不具合だったこと。
- PHPのエラーログには「正常に登録した」というログしか残らず、実際の取得失敗自体は記録されないこと。
- ローカル環境(Docker)にはループバック接続を妨げる要因がなく、まったく再現しなかったこと。
そして多くの一般的なサーバーでは、フルURLを渡しても普通に動いてしまうこと。
今回はたまたまこの環境だけ引っかかった、という運の悪さもありました。
コードとしてはどこにも明確な間違いはありません。むしろ、特定のサーバー環境だけで起こる、見えないバグでした。
add_editor_style()にはフルURLではなく相対パスを渡すのが安全というのは、WordPress公式ドキュメントでも基本とされている書き方ではあるようです。
ただ実際にハマってみるまでは、なぜそう書くべきなのか全くピンときていませんでした。
「フロントは正常なのに管理画面だけおかしい」という症状が出たときは、クラシックエディタとブロックエディタで挙動に差がないか比較してみると、切り分けの大きな手がかりになるというのも今回わかりました。
PHP側のログが正常でも、それはあくまで「関数を呼び出した」ことの証明でしかありません。
その後にブラウザ/サーバー間で何が起きているかは別途確認が必要になります。
環境差分の調査は、プラグイン → バージョン → キャッシュ → サーバー機能、と一つずつ機械的に消していくのが結局一番確実でした。
まとめ
add_editor_style()にはフルURLではなく相対パスを渡す- 通常のCSS(
wp_enqueue_style())はブラウザがURLに直接アクセスして取得するだけなので、ループバック接続は関係なく正常に読み込まれる - 一方
add_editor_style()はブロックエディタのiframeに埋め込むため、サーバー自身がCSSの中身を取得しに行く必要がある(ループバック接続)。これだけがピンポイントでブロックされ、フロント側は無事だった - 「フロントは正常、管理画面だけおかしい」という症状が出たら、クラシックエディタとブロックエディタで挙動に差がないか比較する
- PHPのログが正常でも、それは「関数が呼ばれた」証明にすぎない。その後にブラウザ/サーバー間で何が起きているかは別途確認が必要
- 環境差分の調査は、プラグイン → バージョン → キャッシュ → サーバー機能の順に一つずつ機械的に消していくのが結局一番確実
移行作業自体は特に問題なく終わっていたのに、パスの書き方ひとつでまるまる1時間以上溶けてしまった。
原因がわかってしまえば一行変えるだけの、拍子抜けするような話です。
でも実際に踏んでみるまでは、公式ドキュメントの「相対パスを推奨」という一文の意味も全然ピンときていませんでした。
地味なところに落とし穴があるからこそ、こうやって記録に残しておく意味があると思っています。