WordPressで保存のスピナーが止まらない|原因はCocoonブログカードのURL大量検出だった

WordPress
この記事は約10分で読めます。
WordPressの投稿画面で保存ボタンを押してもスピナーが止まらない。特定の記事だけで起きるなら、本文中のURL単独行がCocoonのブログカード処理を大量に発火させている可能性があります。URLの先頭に「!」を付けるだけで解決しました。 ある日、ブログ記事の下書き保存を押したら、くるくる回るスピナーがいつまで経っても止まりません。エラーメッセージも出ない。画面も遷移しない。ブロックエディタでもクラシックエディタでも同じ。ただし他の記事は問題なく保存できる。 スピナーが回り続けている画面

「更新中…」のスピナーが回り続ける。エラーも出ないので原因が掴みにくい

「サーバー障害?プラグイン?」と焦ってWAFの無効化、セキュリティプラグインの順次停止、キャッシュクリアを試しましたが全部ハズレ。最終的にたどり着いた犯人は、Cocoonのブログカード機能でした。

環境と症状

テーマはCocoon(子テーマ使用)、サーバーはXserver。「特定の記事だけで発生する」という点が原因特定の鍵になりました。
項目 内容
テーマ Cocoon(子テーマ使用)
サーバー Xserver
セキュリティ系プラグイン Wordfence / SiteGuard / BBQ Firewall
再現範囲 特定の1記事だけ(他は正常に保存可能)
サイト全体の障害ならサーバーやWordPress本体を疑いますが、特定記事だけならその記事の本文に何らかのトリガーがあると考えるのが自然です。 実際にその記事だけを見て「他の記事と何が違うか」を考えたとき、最初は記事の長さや画像の数を疑いました。しかし、もっと長い記事でも正常に保存できている。違いは記事の量ではなく「書き方」にありました。

原因:URL単独行がブログカード処理を大量発火させていた

コードブロック内に「URLだけの行」が数十行並んでいたことが原因でした。CocoonとWordPressのoEmbed処理がそれらを全件検出し、タイトル取得やOGP解析を試みてタイムアウトしていました。 「この記事だけにある特徴は何か?」と考えたとき、気づいたのが<pre><code>の中に大量のURLが1行ずつ並んでいた点でした。 Cocoonには、本文中にURLだけを1行で書くと自動でブログカードにする機能があります。WordPress本体にもoEmbedという自動埋め込み機能がある。この2つが、コードブロック内のURL単独行もすべて検出し、各URLに対してタイトル取得やOGP解析を試みていた。数十行分の処理が積み上がって保存がタイムアウトし、スピナーが止まらない状態になっていたのです。 具体的には、ブログカード処理ではURLごとに相手サーバーへHTTPリクエストを送り、HTMLを取得してtitleタグやog:titleを解析します。1件あたり数百ミリ秒〜数秒かかるこの処理が30件、50件と積み上がると、サーバーのmax_execution_time(デフォルト30秒)を簡単に超えます。結果として保存リクエスト自体がタイムアウトし、スピナーが回り続ける状態になります。 DevToolsのNetworkタブ(リクエストがpendingのまま)

DevToolsのNetworkタブ。リクエストがpendingのまま返ってこない

なぜコードブロック内のURLまで検出されるのか

<pre><code>の中だから処理対象にならないだろう」と思っていましたが、実際には検出されました。 これはWordPressの投稿保存の処理順序に起因しています。WordPressはコンテンツを保存する際、the_contentフィルターを通す前の段階でoEmbedの検出処理を実行します。この時点ではHTMLタグの構造解析が完了しておらず、テキスト全体を対象にURL単独行のパターンマッチを行います。つまり<pre>タグの中か外かは区別されません。 Cocoonのブログカード処理も同様のタイミングで動作するため、コードブロック内に書かれたURLであっても「URLだけの行」というパターンに一致すれば処理対象になります。 これは仕様上の挙動であり、バグとは言い切れません。ただ、開発者がコードサンプルとしてURLの一覧を掲載するケースは十分にありえるので、この挙動を知らないと今回のような問題にハマります。

DevToolsで原因を確認する方法

ブラウザのDevTools(F12)を使えば、保存時に何が詰まっているかを自分の目で確認できます。 同じ症状が出たら、以下の手順で原因の切り分けができます。
  1. 投稿編集画面を開いた状態でF12キー(MacはCmd + Option + I)を押す
  2. DevToolsの「Network」タブを選択する
  3. 保存ボタンを押す
  4. Networkタブに表示されるリクエストを観察する
保存時にwp-json/wp/v2/postsadmin-ajax.phpへのリクエストがpending(保留中)のまま長時間返ってこない場合、サーバー側で重い処理が走っていることがわかります。さらにリクエストの「Response」タブを開くと、タイムアウトのエラーメッセージやHTTP 504が記録されていることもあります。 この情報があるだけで「ブラウザ側の問題ではなくサーバー側の処理に時間がかかっている」と判断でき、原因の切り分けが一気に進みます。 もしpendingではなくレスポンスがすぐに返ってきているのに画面が進まない場合は、サーバー側ではなくブラウザ側(JavaScriptのエラーなど)が原因の可能性があります。その場合はDevToolsの「Console」タブにエラーが出ていないか確認してください。

解決策:URLの先頭に「!」を付ける

Cocoonにはブログカード化を無効にする公式の方法があります。URLの先頭に「!」を付けるだけです。 これでURLはリンクとして機能しますが、ブログカード処理の対象からは外れます。

VSCodeの正規表現で一括置換する

URLが数十行あっても、VSCodeの正規表現置換を使えば一瞬です。 検索(正規表現ON): 置換: すでに「!」が付いている行はスキップし、行頭のインデントも維持します。 元に戻したい場合は、検索を^(\s*)!(https?:\/\/[^\s<>"']+)\s*$、置換を$1$2にすれば「!」だけ除去できます。

Cocoon設定で外部ブログカードをOFFにする方法も

Cocoon設定のブログカード設定画面

Cocoon設定 → ブログカード → 外部ブログカード設定

Cocoon設定 →「ブログカード」→「外部ブログカード設定」で外部ブログカードを無効化できます。外部サイトURLが多い記事を頻繁に書くなら検討してもいい方法です。内部ブログカードはONのまま、外部だけOFFにすることも可能です。 ただし、この設定はサイト全体に適用されます。他の記事で外部リンクのブログカードを使いたい場面がある場合は「!」方式で記事単位で制御するほうが柔軟です。

同じ症状で別の原因だった場合のチェックリスト

URL単独行が原因でなかった場合は、WAF・セキュリティプラグイン・データ量・PHPタイムアウトの順に確認してください。 WAF(サーバー側ファイアウォール):Xserver、ConoHa WING、ロリポップなどのWAFが投稿内容をブロックしていることがあります。特にPHPコードやSQLクエリを含む技術記事は誤検知されやすい傾向があります。Xserverの場合はサーバーパネル →「WAF設定」から一時的にOFFにして保存を試してください。保存できたらWAFのログで該当ルールを確認し、そのルールだけ除外設定にするのが安全です。WAFを無効化したまま放置するとセキュリティリスクが生じるため、検証後は必ず有効に戻してください。WAFの誤検知については「XserverのWAF誤検知でコード記事が上がらない…MarsEditで投稿した実体験」でも詳しく解説しています。 セキュリティプラグイン:Wordfence、SiteGuard、BBQ Firewallなどを1つずつ無効化して原因を特定します。すべて無効にしても直らなければセキュリティプラグインは無関係なので、すぐに有効に戻してください。特にWordfenceのファイアウォール機能は、投稿内容にSQLのINSERTDELETE文のような文字列が含まれるとブロックすることがあります。 投稿のデータ量:極端に長い記事や大量の画像を含む記事は、POSTデータのサイズ制限に引っかかることがあります。記事内容を半分に減らして保存を試し、通ればデータ量が原因です。php.inipost_max_size(デフォルト8M〜32M)とupload_max_filesizeを確認してください。Xserverの場合はサーバーパネル →「php.ini設定」から変更できます。 PHPのタイムアウト設定:サーバーのmax_execution_timeが短く設定されていると、処理完了前にタイムアウトします。Xserverのデフォルトは30秒ですが、php.ini設定から変更可能です。ただし、タイムアウトの引き上げは応急処置であり、処理が重い原因自体を特定するのが本筋です。 プラグインの競合:上記のいずれでも解決しない場合は、プラグイン同士の競合が考えられます。すべてのプラグインを無効化して保存を試し、成功したら1つずつ有効に戻して原因を特定してください。時間はかかりますが、最も確実な切り分け方法です。

よくある質問

この問題に関して寄せられやすい疑問をまとめました。

「!」を付けるとURLはリンクにならないのですか?

リンクとしては機能します。「!」はCocoonのブログカード処理だけを無効にするもので、URLのクリック動作には影響しません。ブログカード形式(アイキャッチ付きのカード表示)ではなく、通常のテキストリンクとして表示されます。

ブロックエディタのURLブロックでも同じ問題は起きますか?

起きる可能性があります。ブロックエディタでもURL単独行のパターンマッチは同様に走るため、URLブロックやカスタムHTMLブロックの中でもURL単独行は検出対象になりえます。ブロックの種類に関わらず、URLが多い場合は「!」を付けておくのが安全です。

内部リンク(自サイトのURL)でも発生しますか?

はい。内部ブログカードの処理でも同様にHTTPリクエストが発生します。自サイトへのリクエストなので応答は速いことが多いですが、数十件が積み重なればタイムアウトの原因になりえます。内部URLでも大量にリスト掲載する場合は「!」を付けてください。

再発防止のルール

URLをリスト形式で大量に載せる記事では、最初から「!」を付けて貼る。これだけで再発を防げます。
  1. URLを大量に載せる記事では、最初から「!」付きで貼る
  2. ブログカードは「本当に見せたいリンク」だけに使う
  3. URLが多い記事は、保存前にURL単独行がないかチェックする
  4. 数が多い場合はVSCodeの一括置換を使ってから貼り付ける
URL単独行が20行を超える記事では、たとえコードブロック内であっても念のため「!」を付けておくのが安全です。目安として、5〜10行程度なら保存に数秒余計にかかる程度で済むことが多いですが、相手サーバーの応答速度にも左右されるため一概には言えません。

まとめ

保存のスピナーが止まらず、特定の記事だけで起きるなら、本文中のURL単独行がCocoonのブログカード処理を大量発火させている可能性を疑ってください。 URLの先頭に「!」を付けるだけでブログカード化を無効にできます。サーバー障害でもプラグイン競合でもなく、本文の書き方がトリガーになっているケースがある。「特定の記事だけ」という条件は、原因が本文にあることを示す最大のヒントです。 コードブロック内のURLであっても、WordPressの処理順序上ブログカードの検出対象になりえます。この挙動はバグではなく仕様ですが、知らなければ原因の特定に相当苦労します。 原因の切り分けにはDevToolsのNetworkタブが役立ちます。保存リクエストがpendingのまま返ってこないなら、サーバー側で何か重い処理が走っている証拠。その視点を持つだけで、次に同じ症状が出たときの対応スピードは格段に上がります。 なお、今回のケースではURL単独行が原因でしたが、同じ「スピナーが止まらない」という症状はWAFの誤検知やセキュリティプラグインのブロック、PHPのタイムアウトなど複数の原因で起こりえます。チェックリストの項目を上から順に試せば、たとえ原因が違っても効率よく切り分けられるはずです。

コメント

タイトルとURLをコピーしました