URLの先頭に「!」を付けたら、保存できるようになりました。
それまで、その記事だけは何度保存ボタンを押してもスピナーが回り続けて、いつまでも終わりませんでした。
検証環境:WordPress 6.8.3 / Cocoon 2.8.9(子テーマ使用) / Xserver / PHP 8.3.21(2026年1月時点)
保存ボタンを押しても終わらない
下書きを保存しようとしたら、「更新中」のスピナーが回ったまま止まらなくなりました。エラーメッセージは出ず、画面も先に進みません。ブロックエディタでもクラシックエディタでも同じで、ほかの記事は普通に保存できていました。

最初はサーバーかプラグインだろうと思い、WAFを一時的に切る、セキュリティ系のプラグインを1つずつ止める、キャッシュを消す、と順に試しました。当時入れていたのはWordfence、SiteGuard、BBQ Firewallの3つです。どれを止めても変わらず、どれも外れでした。
サイト全体で起きているならサーバーやWordPress本体を疑うところですが、起きているのはこの1記事だけです。だとすると、この記事の本文の中に何かきっかけがあるはずだ、と考え直しました。
最初に疑ったのは長さと画像の数です。ただ、もっと長い記事も、画像の多い記事も問題なく保存できていました。量ではなく、書き方のどこかが違うのだろうと思って、本文を上から見ていきました。
この記事にだけあったもの
見つけたのは、コードブロックの中にURLが1行に1つずつ、数十行並んでいる箇所でした。こんな形です。
| 1 2 3 4 | https://example.com/page1 https://example.com/page2 https://example.com/page3 ...(数十行続く) |
Cocoonには、本文にURLだけを1行で書くと自動でブログカードにする機能があります。WordPress本体にも、URLだけの行を見つけると埋め込み(oEmbed)に変える仕組みがあります。どちらも、相手のサイトにアクセスしてタイトルやOGPの情報を取りに行きます。
1件ごとに相手のサーバーへリクエストを送り、返ってきたHTMLからtitleやog:titleを読む処理なので、1件で数百ミリ秒から数秒かかることがあります。それが数十件積み重なれば、PHPの max_execution_time(PHPの既定値は30秒)を超えてもおかしくありません。保存のリクエストそのものが時間切れになって、画面側は返事を待ったままスピナーを回し続けていた、というのが今回の見立てです。
なぜコードブロックの中のURLまで拾われるのか
<pre><code> の中に書いたものは、ただの文字として扱われるものだと思っていました。実際には拾われていました。
ここは、ソースを最後まで追い切れていないので、今の理解として書きます。ブロックエディタで保存すると、WordPressはREST APIで保存したあと、表示用に加工した本文(content.rendered)を応答に入れて返します。この加工のときに、記事を表示するときと同じフィルターが動き、WordPressの自動埋め込みもCocoonのブログカードもここで走ります。どちらも「行の中にURLだけがある」というパターンで探していて、その行が <pre> の中か外かまでは見ていないようです。だから、コードブロックの中に並べたURLも、1件ずつ処理の対象になっていたのだと考えています。
仕様どおりの動きで、バグとまでは言えないと思います。ただ、技術記事でURLの一覧をサンプルとして載せることは普通にあるので、知らないとかなり迷います。私は迷いました。
DevToolsで詰まっている場所を見る
同じ症状が出たときは、ブラウザのDevToolsで、保存のときに何が返ってこないのかを先に見ておくと切り分けが早くなります。投稿の編集画面を開いたままF12(Macは Cmd + Option + I)でDevToolsを開き、Networkタブを選んでから保存ボタンを押します。
ブロックエディタなら wp-json/wp/v2/posts/(投稿ID)、クラシックエディタなら post.php へのリクエストが出ます。これがpending(保留中)のまま長く返ってこなければ、サーバー側で重い処理が走っています。しばらく待つと、504などのエラーで終わることもあります。クラシックエディタでは、保存のあとに admin-ajax.php への埋め込みキャッシュのリクエストが出ることもあるので、そちらも一緒に見ておきます。

保存のリクエストが、pendingのまま残っていました
逆に、レスポンスはすぐ返ってきているのに画面が先に進まないなら、サーバーではなくブラウザ側、たとえばJavaScriptのエラーを疑います。その場合はConsoleタブに赤いエラーが出ていないかを見てください。
今回はpendingのまま返ってこない側だったので、サーバーの中で何かを待っている、という方向で考えることができました。
URLの先頭に「!」を付ける
Cocoonには、URLの先頭に「!」を付けるとブログカードにしない、という書き方が用意されています。!https://example.com/page1 のように書くだけです。WordPress本体の自動埋め込みも「httpで始まる行」を探しているので、「!」で始まる行は対象から外れます。
コードブロックの中の数十行にすべて「!」を付けたところ、保存は普通に終わるようになりました。
「!」を付けても、表示がブログカードではなく普通のテキストになるだけで、リンクとしてはそのまま使えます。自分のサイトのURLでも同じことが起きます。内部ブログカードも自サイトに取りに行くので、1件ごとの応答は速くても、数が多ければ積み重なります。ブロックエディタで貼ったURLが自動で埋め込みブロックになる場合や、カスタムHTMLブロックの中にURLだけの行を並べた場合も、同じ形で拾われる可能性があります。
数十行に手で「!」を付けるのは大変なので、私はVSCodeの正規表現置換でまとめて付けました。置換の式は記事の最後に置いています。
サイト全体で外部サイトのブログカードを止めたいなら、Cocoon設定の「ブログカード」タブにある外部ブログカード設定で無効にできます。内部ブログカードは有効のまま、外部だけを止めることもできます。

ただ、この設定はサイト全体に効きます。ほかの記事で外部サイトのブログカードを使いたい場面があるなら、記事ごとに「!」で外すほうが融通が利きます。私は「!」のほうにしました。
同じ症状で、URLが原因でなかったら?
スピナーが止まらないという症状は、ほかの原因でも起きます。今回最初に疑ったものも含めて、確かめる順に並べておきます。
WAF(サーバー側のファイアウォール)
XserverやConoHa WING、ロリポップなどのWAFが、投稿の中身を攻撃と見なしてブロックすることがあります。PHPのコードやSQLを含む技術記事は、とくに引っかかりやすいです。Xserverなら、サーバーパネルの「WAF設定」で一時的にオフにして保存を試せます。保存できたらWAFのログで該当のルールを見て、そのルールだけを除外するのが安全です。確認が終わったら、WAFは必ず有効に戻してください。
Xserverでの誤検知は「WordPressでコード記事を更新したら501 Not Implemented」で詳しく書いています。
セキュリティ系のプラグイン
Wordfence、SiteGuard、BBQ Firewallなどを1つずつ止めて試します。全部止めても直らなければプラグインは関係ないので、すぐに有効に戻してください。Wordfenceのファイアウォールは、本文にSQL文のような文字列(INSERTやDELETEなど)が含まれていると止めることがあります。
投稿のデータ量
極端に長い記事や画像の多い記事は、送信できるデータの上限に当たることがあります。本文を半分に減らして保存できれば、量が原因です。php.iniの post_max_size と upload_max_filesize を確認してください。既定値はサーバーによって違います。Xserverならサーバーパネルの「php.ini設定」から変えられます。
PHPの実行時間
max_execution_time が短いと、処理が終わる前に打ち切られます。php.iniの設定で延ばせますが、延ばすのは応急処置です。何が重いのかを突き止めるほうが先だと思います。今回も、延ばしていたら保存は通ったかもしれませんが、URLの行が原因だとは気づかなかったはずです。
プラグイン同士の競合
ここまでで直らなければ、プラグインをすべて止めて保存を試し、通ったら1つずつ戻して原因を探します。時間はかかりますが、いちばん確実な方法です。
VSCodeでまとめて「!」を付ける置換
検索欄の正規表現をオンにして、次の式で検索します。すでに「!」が付いている行は飛ばし、行頭の字下げはそのまま残します。
| 1 | ^(\s*)(?!!)(https?:\/\/[^\s<>"']+)\s*$ |
置換欄には次を入れます。
| 1 | $1!$2 |
元に戻したいときは、検索を次の式、置換を $1$2 にすると「!」だけが消えます。
| 1 | ^(\s*)!(https?:\/\/[^\s<>"']+)\s*$ |
それ以来、URLを何行も並べる記事では、最初から「!」を付けて貼るようにしています。ブログカードにするのは、本当に読んでほしいリンクだけです。
あなたの下書きにも、コードブロックの中に並んだままのURLは残っていませんか。


コメント