WP Rocket 多言語キャッシュ:下書き保存のたびに翻訳版のキャッシュが消える原因と解決

WP Rocketの多言語キャッシュは、TranslatePressの翻訳版アドレスごとに言語フォルダの下へ別々に保存されるページキャッシュです。Nginx + Apache構成のWordPressサーバーで、下書きを保存するたびに翻訳版キャッシュが言語フォルダごと消える原因をWP Rocketのコードで追跡した結果、公開されていない投稿を翻訳版キャッシュの削除から外し、直接削除したアドレスはプリロードに改めて知らせるコードで解決しました。
同じサーバー内で測った最初のバイトまでの時間は、翻訳版キャッシュがあるときは0.015秒、ないときは2.492秒でした。言語フォルダが空になると、誰かがそのアドレスを開き直してキャッシュが作られるまで、翻訳版の訪問者はこの遅い応答を受けることになります。
1️⃣ WP Rocket 多言語キャッシュの構造:言語別フォルダと翻訳版の削除
翻訳版は原文の投稿を翻訳して見せる画面なので、キャッシュも原文とひとまとまりだと考えがちですが、WP Rocketは翻訳版のアドレスを別のページとしてキャッシュします。キャッシュフォルダの下には原文の投稿フォルダと並んで[en]・[ja]のような言語フォルダができ、翻訳版の投稿はその中に翻訳スラッグの名前で保存されます。
投稿を編集すると、WP RocketのTranslatePress連携がその投稿の翻訳版キャッシュも一緒に削除します。公式ドキュメントでも、通常の編集ではすべての翻訳版を、TranslatePressの翻訳エディターで直した場合は変わった言語だけを削除すると案内しています。
// wp-rocket/inc/ThirdParty/Plugins/I18n/TranslatePress.php (3.23.5.1)
'post_updated' => 'clear_post_languages', // 投稿が保存されるたびに実行
public function clear_post_languages( $post_id ) {
// …
$default_permalink = get_permalink( $post_id ); // 投稿の状態は確認しない
foreach ( $trp_settings['translation-languages'] as $language ) {
if ( $language === $trp_settings['default-language'] ) {
continue;
}
$clear_urls[] = $converter->get_url_for_language( $language, $default_permalink, '' );
}
// …
rocket_clean_files( $clear_urls );
}連携関数は投稿のアドレスを言語ごとに翻訳版のアドレスへ変換してから、キャッシュファイルを整理する関数[rocket_clean_files]に渡します。この過程に投稿の状態を確認する行がないため、公開済みの投稿でも下書きでも同じ手順で翻訳版のアドレスを作って削除することになります。
2️⃣ 下書きの保存:言語フォルダ全体の削除
下書きの保存は翻訳版キャッシュと無関係ではありません。公開前の投稿のパーマリンクはスラッグではなく[?p=投稿番号]の形で、TranslatePressがこのアドレスを[/en/?p=投稿番号]のように言語パスを付けたアドレスへ変換するためです。
[rocket_clean_files]はアドレスからクエリを除き、パスだけでキャッシュの場所を探します。残ったパスが[/en/]ならその言語のキャッシュフォルダ全体になり、対象がフォルダなら下位フォルダまで再帰的に削除します。
// wp-rocket/inc/functions/files.php (3.23.5.1) — rocket_clean_files() の中
$parsed_url = get_rocket_parse_url( $url ); // パスとクエリを分ける
// …
$entry = $dir . $parsed_url['path']; // …/example.com/en/
// …
if ( $filesystem->is_dir( $entry ) ) {
rocket_rrmdir( $entry, [], $filesystem ); // フォルダごと再帰的に削除
} else {
$filesystem->delete( $entry );
}トップページも同じ経路を通ります。トップのアドレス[/]が[/en/]に変換されるため、公開済みのトップページを保存しても言語フォルダ全体が削除されます。下書きのタイトルだけ直して保存し直しても4言語のキャッシュフォルダがすべて空になり、下書きの保存が集中した一晩で同じ削除が30回以上繰り返されたことを、保存時刻とキャッシュ削除の記録で確認しました。
コード上、エディターの自動保存も同じ経路です。公開済みの投稿を編集している間に保存されるリビジョンも[?p=]の形のアドレスを使います。
Nginx + Apache構成のテストサイトで、公開済みの投稿をエディターで直しながら言語フォルダのファイル数を確認しました。最初の自動保存では英語・日本語フォルダがそれぞれ20個のままで、1分後の2回目の自動保存で両方のフォルダが0個になりました。
最初の自動保存はリビジョンを新しく作り、2回目からは同じリビジョンを直して保存するため[post_updated]が実行されます。最初の自動保存の後も本文を直し続けると、保存ボタンを押さなくても翻訳版キャッシュが言語フォルダごと削除されます。
WP Rocketの公式リポジトリにも同じ症状が2023年8月31日からIssue #6130として開かれています。修正するプルリクエスト#7616は2025年9月に出されましたがマージされておらず、2026年10月2日に出た3.23.5.1の変更履歴にも修正は入っていません。
3️⃣ 解決コード:公開済みの投稿だけ翻訳版のキャッシュを削除
公式の修正が出るまでは、連携関数を外し、アドレスが確実な場合にだけ翻訳版を削除する関数で置き換えることになります。Issueのコメントに投稿された回避策も、公開されていない投稿なら連携関数からすぐ抜ける同じ方向です。
ただし、投稿の状態だけを確認する条件では2つのケースが残ります。トップページは公開状態でも保存すると言語フォルダ全体を消し、公開済みの投稿を下書きに戻すと翻訳版キャッシュが削除されないため、戻した投稿が翻訳版のアドレスで開き続けます。
<?php
/**
* Plugin Name: Rocket TranslatePress Draft Fix
* Description: WP Rocket TranslatePress連携の翻訳版キャッシュ削除を、アドレスが確実な場合に限定
*/
// 1. WP Rocketの連携関数をpost_updatedから外して保管
add_action( 'init', function () {
global $wp_filter;
if ( empty( $wp_filter['post_updated'] ) ) {
return;
}
foreach ( $wp_filter['post_updated']->callbacks as $priority => $callbacks ) {
foreach ( $callbacks as $cb ) {
$fn = $cb['function'];
if ( is_array( $fn ) && is_object( $fn[0] )
&& 0 === strpos( get_class( $fn[0] ), 'WP_Rocket\\' )
&& false !== stripos( get_class( $fn[0] ), 'TranslatePress' ) ) {
remove_action( 'post_updated', $fn, $priority );
$GLOBALS['rtdf_original'] = $fn;
}
}
}
}, 9999 );
// 2. 代わりの関数
add_action( 'post_updated', function ( $post_id, $after, $before ) {
if ( empty( $GLOBALS['rtdf_original'] ) || ! class_exists( 'TRP_Translate_Press' ) ) {
return;
}
$trp = TRP_Translate_Press::get_trp_instance();
$converter = $trp->get_component( 'url_converter' );
$settings = $trp->get_component( 'settings' )->get_settings();
$languages = array_diff( $settings['translation-languages'], [ $settings['default-language'] ] );
// トップページ — 言語フォルダではなく言語ホームのキャッシュファイルだけ
if ( 'page' === get_option( 'show_on_front' ) && (int) get_option( 'page_on_front' ) === (int) $post_id ) {
$urls = [];
add_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
foreach ( $languages as $lang ) {
$home = $converter->get_url_for_language( $lang, home_url( '/' ), '' );
$dir = WP_ROCKET_CACHE_PATH . wp_parse_url( $home, PHP_URL_HOST ) . wp_parse_url( $home, PHP_URL_PATH );
array_map( 'unlink', glob( $dir . 'index*.html*' ) ?: [] );
$urls[] = $home;
}
remove_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
do_action( 'rocket_after_clean_terms', $urls ); // プリロードに通知(5️⃣)
return;
}
// 公開済みの投稿の保存 — WP Rocketの元の関数をそのまま
if ( 'publish' === $after->post_status ) {
call_user_func( $GLOBALS['rtdf_original'], $post_id );
return;
}
// 公開 → 下書き・非公開・ゴミ箱 — 取り下げる前のアドレスの翻訳版だけ
if ( 'publish' === $before->post_status ) {
$old = get_permalink( $before );
$urls = [];
add_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
foreach ( $languages as $lang ) {
$urls[] = $converter->get_url_for_language( $lang, $old, '' );
}
remove_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
rocket_clean_files( $urls );
}
// それ以外(下書き・リビジョン・自動保存)は翻訳版のアドレスがないので削除しない
}, 10, 3 );最初の部分はWP Rocketが登録した連携関数を見つけて[post_updated]から外し、元の関数は保管しておきます。代わりの関数は、トップページなら言語ホームのキャッシュファイルだけを消し、公開済みの投稿なら保管した元の関数をそのまま実行し、公開済みの投稿を取り下げた場合は取り下げる前のアドレスで翻訳版を探して削除します。
[wp-content/mu-plugins/]フォルダにPHPファイルとして保存すると有効化なしですぐに適用され、ファイルを消せばWP Rocket本来の動作に戻ります。同じ動作のコードで試した結果、下書きを保存し直しても言語フォルダのファイル数はそのままで、公開済みの投稿を保存し直すとその投稿の原文と翻訳版のフォルダだけが消えました。
上のコードを入れる前と入れた後で、同じ環境の4つのケースについて言語フォルダのファイル数を比べました。英語・日本語フォルダには、言語ホームと投稿4本のPC・モバイルのキャッシュがそれぞれ20個ありました。
コードを入れる前は、下書きの保存、トップページの保存、公開の取り消しの3つのケースすべてで両方のフォルダが0個になり、公開済みの投稿の保存だけがその投稿の翻訳版キャッシュを削除しました。
コードを入れた後は、下書きの保存とエディターの自動保存で20個がそのまま残りました。トップページの保存では言語ホームのキャッシュ4個、公開済みの投稿の保存と公開の取り消しではその投稿の翻訳版4個だけが削除されて16個が残り、取り下げた投稿の英語の翻訳版アドレスは404を返しました。
| ケース | 修正前 | コードを入れた後 | 入れた後に削除されたキャッシュ |
|---|---|---|---|
| 下書きの保存 | 20 → 0 | 20 → 20 | なし |
| 公開済みの投稿の保存 | その投稿の翻訳版だけ削除 | 20 → 16 | その投稿の翻訳版4個 |
| トップページの保存 | 20 → 0 | 20 → 16 | 言語ホームのキャッシュ4個 |
| 公開の取り消し | 20 → 0 | 20 → 16 | その投稿の翻訳版4個・翻訳版のアドレスは404 |
4️⃣ プリロード再生成の漏れ:キューに残る完了状態
下書きの保存を防いでも、WP Rocketの関数の削除シグナルなしに消したキャッシュファイルはプリロードが再び埋めません。 WP Rocketのプリロードは、アドレスごとに状態を記録するキューのテーブルを使い、リクエスト予定のアドレスは待機、リクエストを送ったアドレスは進行中、キャッシュが作られたアドレスは完了に変わり、3分以内に終わらなければ失敗になります。
完了したアドレスが待機に戻るのは、キャッシュの寿命が尽きて消えたファイル、WP Rocketが削除しながら削除シグナルを出したアドレス、失敗してから12時間が過ぎたアドレスの3つです。[rocket_clean_files]の3番目の引数にfalseを渡すか、ファイルを直接削除するとこのシグナルが出ないため、キューには完了のまま残り、キャッシュファイルだけが消えます。
3番目の引数は、削除シグナルを受けてほかのキャッシュを続けて消すコードが、繰り返し呼び出しを防ぐためにfalseで渡すことになります。翻訳版キャッシュを照合した時点で、英語の翻訳版65個のうち3個、日本語の翻訳版65個のうち7個が、キューには完了のまま残ってキャッシュファイルだけがありませんでした。
WP Rocketのサポートチームの貢献者も、Issue #6130のコメントで、キャッシュを消した後にテーブルのアドレスが完了のまま残って状態がずれることを認めました。テーブルとキャッシュファイルを定期的に合わせようという提案(#2939)は、2020年に実装されないまま閉じられています。
5️⃣ プリロードへの再登録:直接削除したアドレスをキューに知らせる
直接削除したアドレスは、削除した直後にプリロードへ知らせる1行で解決します。プリロードは[rocket_after_clean_terms]シグナルを受けると、受け取ったアドレスを待機状態に戻し、テーブルにないアドレスは新しく追加します。
// 削除シグナルなしで消した後 — 消したアドレスの配列をプリロードのキューに再登録
rocket_clean_files( $urls, null, false ); // 3番目の引数 false = 削除シグナルなし
do_action( 'rocket_after_clean_terms', $urls ); // プリロードがこれらのアドレスを待機(pending)にWP Rocketの中でこのシグナルを受け取るのはプリロードのキュー関数とSpinUpWPホスティング連携だけで、プリロード関数はキューの行を直すだけでWP Rocketのキャッシュファイルを消したり削除シグナルを再び出したりしないため、繰り返し呼び出しは起きません。キャッシュをWP Rocketの関数だけで消し、3番目の引数を既定値のままにしているサイトは、すでにシグナルが出るのでこの行は必要ありません。
この行を入れた後、翻訳版が4言語ある投稿1本のキャッシュを消すと、2秒以内にキューの行4つが待機に変わりました。cronを1分ごとに実行するサーバーで2回試し、101秒後と153秒後に翻訳版のPC・モバイルのキャッシュファイル8個がすべて作り直されました。確認はキューのテーブルの状態とキャッシュフォルダのファイルで行います。
# キューのテーブルでのそのアドレスの状態(テーブル名の前の wp_ はサイトの接頭辞)
wp db query <<'SQL'
SELECT url, status, modified FROM wp_wpr_rocket_cache WHERE url LIKE '%/en/sample-post-en'
SQLurl status modified
https://example.com/en/sample-post-en pending 2026-10-17 09:10:48# 1分ほど後にキャッシュファイルが再び作られたか
ls -l wp-content/cache/wp-rocket/example.com/en/sample-post-en/total 240
-rw-r--r-- 1 example nobody 95084 Oct 17 18:12 index-https.html
-rw-r--r-- 1 example nobody 22352 Oct 17 18:12 index-https.html_gzip
-rw-r--r-- 1 example nobody 95083 Oct 17 18:12 index-mobile-https.html
-rw-r--r-- 1 example nobody 22348 Oct 17 18:12 index-mobile-https.html_gzip削除した直後は状態が[pending]に変わり、プリロードがリクエストを送ってキャッシュファイルができると[completed]になります。状態が[completed]なのにキャッシュファイルがなければ、そのアドレスはシグナルなしで消されたものです。
トップページを保存して上のコードが言語ホームのキャッシュファイルを消してシグナルを出すと、英語・日本語のホームのアドレスが再びキューに入りました。言語ホームのPC・モバイルのキャッシュは、遅くとも2回目のcron実行で作り直されました。
2回試して、保存から最後のキャッシュファイルまで47秒と91秒かかりました。
🔢 FAQ & おすすめコンテンツ









ℹ️ 提携について
当サイトのコンテンツにはアフィリエイトリンクが含まれています。訪問者がこのリンクを通じて商品やサービスを購入すると、当サイトは販売元から手数料を受け取ります。この過程において、購入者が支払う金額(イベント割引適用で金額が下がります↓)は上昇しません。掲載されている価格・割引・在庫情報は作成時点のものであり、実際とは異なる場合がありますので、ご購入前に販売元にて最終確認をお願いいたします。商品の選定および評価は独自の基準に基づいて行われており、手数料の支払いの有無は紹介順や評価内容に影響を与えません。






