Многоязычный кеш WP Rocket: почему при сохранении черновика исчезает кеш переводов и как это исправить

Уведомление о партнёрских ссылках v3.1
Уведомление о партнёрских ссылках v3.2 (мобильная версия)
Тестовая среда Семейство RHELAlmaLinux 9.8Nginx 1.28 + Apache 2.4WP Rocket 3.23.5.1TranslatePress 3.3.8Проверено 2026-10-17

Многоязычный кеш WP Rocket — это кеш страниц, который хранится отдельно в языковой папке для каждого адреса перевода TranslatePress. На сервере WordPress с Nginx + Apache по коду WP Rocket была прослежена причина, по которой кеш переводов исчезает вместе со всей языковой папкой при каждом сохранении черновика, и проблема была решена кодом, который исключает неопубликованные записи из очистки кеша переводов и заново сообщает предзагрузке об адресах, удалённых вручную.

Время до первого байта, измеренное внутри того же сервера, составило 0,015 с при наличии кеша перевода и 2,492 с без него. Когда языковая папка опустевает, посетители переводов получают такой медленный ответ, пока кто-нибудь снова не откроет этот адрес и кеш не будет создан заново.

1️⃣ Устройство многоязычного кеша WP Rocket: папки по языкам и очистка переводов

Перевод — это страница, которая показывает исходную запись на другом языке, поэтому легко решить, что её кеш связан с оригиналом, но WP Rocket кеширует адреса переводов как отдельные страницы. В папке кеша рядом с папками исходных записей появляются языковые папки вроде [en] и [ja], а переведённые записи хранятся внутри них под переведёнными ярлыками.

wp-content/cache/wp-rocket/example.com/
sample-post/Исходная запись
index-https.html
index-mobile-https.html
en/Английская языковая папка
index-https.htmlГлавная английской версии
sample-post-en/Английский перевод записи (переведённый ярлык)
index-https.html
index-mobile-https.html
ja/Японская языковая папка
index-https.html
sample-post-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=номер записи].

Сохранение черновикаСохранение в редакторе · wp_update_post на сервере · изменение только заголовка
post_updated
clear_post_languages( 123 )Интеграция WP Rocket с TranslatePress
get_permalink()→ https://example.com/?p=123 · запись не опубликована, поэтому ярлыка нет
get_url_for_language()→ https://example.com/en/?p=123 (для ja·es·de так же)
rocket_clean_files()
Параметр ?p=123 отбрасывается→ путь /en/
Расположение кеша→ wp-content/cache/wp-rocket/example.com/en/
Это папка, поэтому удаление рекурсивное→ Весь кеш английских переводов удаляется

[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/], поэтому даже сохранение опубликованной главной страницы удаляет всю языковую папку. Достаточно было изменить только заголовок черновика и сохранить его снова, чтобы папки кеша всех четырёх языков опустели, а по времени сохранений и журналу удаления кеша было установлено, что за одну ночь с частыми сохранениями черновиков такое удаление повторилось более 30 раз.

🧭 Сохранение записей и удаление кеша переводов
WP RocketПоясняющая схема
📝 Сохраняемая запись
📝 Постоянная ссылка
[get_permalink]
/sample-post/Опубликованная запись · адрес с ярлыком
/?p=123До публикации · у ревизий автосохранения так же
/Главная страница
post_updated→
🌐 Адрес перевода
Языковой путь TranslatePress
/en/sample-post-en/
/en/?p=123
/en/
для ja·es·de так же
✅ Путь /en/sample-post-en/ · папка этой записи
❌ ?p=123 отбрасывается · остаётся только путь /en/
❌ Путь /en/ · языковая папка
rocket_clean_files→
🗑️ Удаляемый кеш
① Только папка перевода этой записиen/sample-post-en/Главная языковой версии и переводы других записей остаются
② Вся языковая папкаen/Главная языковой версии · вплоть до переводов других записей · 20 файлов → 0
Постоянная ссылка опубликованной записи — это ярлык, поэтому адрес перевода тоже указывает на эту запись, и удаляется только папка кеша перевода этой записи.
Постоянная ссылка черновика имеет вид [?p=123], поэтому адрес перевода становится [/en/?p=123]. Когда [rocket_clean_files] отбрасывает строку запроса, остаётся только [/en/], и удаляется вся английская языковая папка; ревизии автосохранения в редакторе используют тот же адрес.
Адрес главной страницы [/] превращается в [/en/], поэтому даже сохранение опубликованной главной страницы удаляет всю языковую папку.

Судя по коду, автосохранение в редакторе проходит тот же путь. Ревизии, которые сохраняются, пока вы редактируете опубликованную запись, тоже используют адрес вида [?p=].

На тестовом сайте с Nginx + Apache опубликованная запись редактировалась в редакторе с подсчётом файлов в языковых папках. После первого автосохранения в английской и японской папках оставалось по 20 файлов, а после второго автосохранения через минуту в обеих папках стало 0 файлов.

Первое автосохранение создаёт новую ревизию, а начиная со второго изменяется и сохраняется та же ревизия, поэтому срабатывает [post_updated]. Если продолжать править текст после первого автосохранения, кеш переводов удаляется вместе с языковой папкой даже без нажатия кнопки сохранения.

Тот же симптом открыт в официальном репозитории WP Rocket как issue #6130 с 31 августа 2023 года. Pull request #7616 с исправлением был отправлен в сентябре 2025 года, но не принят, и в журнале изменений версии 3.23.5.1, вышедшей 2 октября 2026 года, исправления тоже нет.

3️⃣ Код исправления: кеш переводов удаляется только для опубликованных записей

Пока нет официального исправления, функцию интеграции отключают и заменяют функцией, которая удаляет переводы только тогда, когда адрес известен наверняка. Обходной путь из комментариев к issue идёт в том же направлении: сразу выходить из функции интеграции, если запись не опубликована.

Однако условие, проверяющее только статус записи, оставляет два случая. Главная страница даже в опубликованном статусе при сохранении удаляет всю языковую папку, а если опубликованную запись вернуть в черновик, кеш её переводов не удаляется, и снятая запись продолжает открываться по адресам переводов.

<?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] и сохраняет исходную функцию. Функция-замена для главной страницы удаляет только файлы кеша главной языковой версии, для опубликованной записи выполняет сохранённую исходную функцию как есть, а при снятии опубликованной записи находит и удаляет переводы по адресу, который был до снятия.

Если сохранить код как PHP-файл в папке [wp-content/mu-plugins/], он начинает действовать сразу, без активации, а при удалении файла WP Rocket возвращается к исходному поведению. Проверка кодом с таким же поведением показала: при повторном сохранении черновика число файлов в языковых папках не изменилось, а при повторном сохранении опубликованной записи были удалены только папки оригинала и перевода этой записи.

До и после добавления этого кода в одном и том же окружении сравнивалось число файлов в языковых папках для четырёх случаев. В английской и японской папках было по 20 файлов кеша для ПК и мобильных устройств — для главной языковой версии и четырёх записей.

До добавления кода сохранение черновика, сохранение главной страницы и снятие с публикации — все три случая — оставляли в обеих папках 0 файлов, и только сохранение опубликованной записи удаляло лишь кеш переводов этой записи.

После добавления кода при сохранении черновика и при автосохранении в редакторе все 20 файлов остались. Сохранение главной страницы удалило только 4 файла кеша главной языковой версии, а сохранение опубликованной записи и снятие с публикации — только 4 файла переводов этой записи, так что осталось 16, а английский адрес перевода снятой записи вернул 404.

СлучайДо исправленияПосле добавления кодаУдалённый кеш после добавления
Сохранение черновика20 → 020 → 20Нет
Сохранение опубликованной записиУдаляются только переводы этой записи20 → 164 файла переводов этой записи
Сохранение главной страницы20 → 020 → 164 файла кеша главной языковой версии
Снятие с публикации20 → 020 → 164 файла переводов этой записи · адрес перевода 404

4️⃣ Предзагрузка не пересоздаёт кеш: статус «завершено», оставшийся в очереди

Даже если сохранение черновиков заблокировано, файлы кеша, удалённые без сигнала удаления от функции WP Rocket, предзагрузка заново не заполняет. Предзагрузка WP Rocket использует таблицу очереди, где для каждого адреса записывается статус: адреса, ожидающие запроса, получают статус «ожидание», адреса с отправленным запросом — «в процессе», адреса с созданным кешем — «завершено», а то, что не завершилось за 3 минуты, — «ошибка».

Завершённый адрес возвращается в ожидание в трёх случаях: его файл удалён по истечении времени жизни кеша, WP Rocket отправил сигнал удаления при очистке или с момента ошибки прошло больше 12 часов. Если передать false третьим аргументом [rocket_clean_files] или удалить файлы напрямую, этот сигнал не отправляется, и исчезают только файлы кеша, а в очереди они по-прежнему значатся завершёнными.

Третьим аргументом false передаёт код, который получает сигнал удаления и затем очищает другой кеш, — так он избегает повторных вызовов. При сверке кеша переводов 3 из 65 английских и 7 из 65 японских переводов значились в очереди завершёнными, хотя их файлов кеша не было.

Участник команды поддержки WP Rocket тоже признал в комментариях к issue #6130, что после очистки кеша адреса в таблице остаются завершёнными и состояние расходится. Предложение периодически сверять таблицу с файлами кеша (#2939) было закрыто в 2020 году без реализации.

5️⃣ Повторная регистрация в предзагрузке: сообщить очереди об адресах, удалённых вручную

Для адресов, удалённых вручную, хватает одной строки, которая сразу после удаления сообщает об этом предзагрузке. Получив сигнал [rocket_after_clean_terms], предзагрузка возвращает переданные адреса в ожидание и добавляет адреса, которых нет в таблице.

// После удаления без сигнала удаления — заново зарегистрировать массив удалённых адресов в очереди предзагрузки
rocket_clean_files( $urls, null, false );          // третий аргумент false = без сигнала удаления
do_action( 'rocket_after_clean_terms', $urls );     // предзагрузка возвращает эти адреса в ожидание (pending)

Внутри WP Rocket этот сигнал принимают только функция очереди предзагрузки и интеграция с хостингом SpinUpWP, а функция предзагрузки лишь обновляет строки очереди, не удаляя файлы кеша WP Rocket и не отправляя сигнал удаления повторно, поэтому повторных вызовов не возникает. Сайтам, которые очищают кеш только функциями WP Rocket и оставляют третий аргумент по умолчанию, эта строка не нужна: сигнал у них уже отправляется.

После добавления этой строки кеш одной записи с переводами на четыре языка был очищен, и за 2 секунды четыре строки очереди перешли в ожидание. В двух проверках на сервере, где cron запускается каждую минуту, все 8 файлов кеша переводов для ПК и мобильных устройств были созданы заново через 101 и 153 секунды. Проверить это можно по статусу в таблице очереди и по файлам в папке кеша.

# Статус этого адреса в таблице очереди (wp_ перед именем таблицы — префикс сайта)
wp db query <<'SQL'
SELECT url, status, modified FROM wp_wpr_rocket_cache WHERE url LIKE '%/en/sample-post-en'
SQL
url	status	modified
https://example.com/en/sample-post-en	pending	2026-10-17 09:10:48
# Появились ли файлы кеша снова примерно через минуту
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], а файла кеша нет, этот адрес был удалён без сигнала.

Когда главная страница была сохранена и код выше удалил файлы кеша главной языковой версии и отправил сигнал, адреса главных английской и японской версий снова попали в очередь. Кеш главной языковой версии для ПК и мобильных устройств был создан заново не позже второго запуска cron.

В двух проверках от сохранения до последнего файла кеша прошло 47 и 91 секунда.

🔢 FAQ & рекомендуемые материалы

Эта статья основана на интеграции WP Rocket с TranslatePress; интеграции с WPML и Polylang не проверялись. В июле 2026 года на форуме поддержки WordPress.org был описан случай, когда в связке другого плагина кеширования и Polylang адрес, в котором остался только параметр запроса, превращался в папку кеша верхнего уровня и удалялся целиком, и его тоже решили кодом, который отключает и оборачивает соответствующий callback.

Это issue #9028, открытый в репозитории WP Rocket 16 октября 2026 года. Предзагрузка записывает кеш по адресу с исходным ярлыком, а не с переведённым, поэтому строки адресов переводов не получают статус «завершено» и время последнего обращения и удаляются вместе с остальными еженедельной очисткой, которая стирает строки без обращений в течение месяца. Этот срок можно продлить фильтром [rocket_preload_delay_delete_non_accessed], пример в официальной документации — 1 год, а черновик исправления #9029 по состоянию на 17 октября 2026 года не принят.

Его можно убрать, когда исправление issue #6130 появится в журнале изменений и функция интеграции начнёт проверять статус записи или адреса с параметрами запроса. Если изменится имя функции или хук, на котором она зарегистрирована, код отключения не найдёт цель и исходное поведение сохранится, поэтому после обновления сохраните один черновик и проверьте, что языковые папки на месте.

🧭 Статьи о WP Rocket Статьи: 16 · Инструменты: 3
📋 Настройка WP Rocket 📄 WP Rocket MCP Server 📄 WP Rocket и URL на корейском 📄 nginx njs 📄 Многоязычный кеш WP Rocket 📄 gclid и кеш
📄 Тест скорости WP Rocket 📝 Плагины кеширования и оптимизации 📝 Пример ускорения сайта
📋 Кеш: определение и все области применения 📄 Кеш сайта & внутренний кеш сервера
📋 Лучший хостинг: TOP 7 📄 Быстрый хостинг 2026
🛠️ Определение WordPress 🛠️ WHOIS-проверка домена 🛠️ Мой IP
Что такое WP Rocket: пять утверждений из статьи с рекомендацией FlyingPress в сравнении с официальными экранами и замерами на сервере
WordPress · WP Rocket
Что такое WP Rocket: основные функции, плюсы и минусы универсального плагина кеширования
Как WP Rocket отдаёт страницы из файлов кеша и сколько он стоит.
Читать →Текущая статья
Настройка WP Rocket: базовые параметры каждого меню и промокод на 20%
WordPress · WP Rocket
Настройка WP Rocket: базовые параметры каждого меню и промокод на 20%
Базовые настройки WP Rocket по каждому меню и цены.
Читать →Текущая статья
WP Rocket MCP Server: управление кешем и настройками через ИИ-инструменты
WordPress · MCP
WP Rocket MCP Server: управление кешем и настройками через ИИ-инструменты
Как управлять кешем и настройками WP Rocket из ИИ-инструментов.
Читать →Текущая статья
Схема прохождения запросов с корейскими URL в WP Rocket
WordPress · WP Rocket
WP Rocket и URL на корейском
Почему nginx не находит файл кеша из-за регистра букв в кодировке запросов с корейскими адресами и как исправить это с помощью njs.
Читать →Текущая статья
Схема переменных nginx njs и путей к файлам кеша
Server · nginx
nginx njs: установка модуля, настройка js_set и применение через reload для создания переменных JavaScript
Как установить модуль njs, который создаёт переменные с помощью функций JavaScript внутри конфигурации nginx, и применить переменные js_set через reload.
Читать →Текущая статья
Схема сохранения записи при многоязычном кеше WP Rocket
WordPress · WP Rocket
Многоязычный кеш WP Rocket
Почему при сохранении черновика кеш переводов удаляется вместе со всей папкой языка, и код, который это предотвращает.
Читать →Текущая статья
Как запрос с рекламным параметром при отсутствии копии в CDN уходит на исходный сервер, проходит из nginx в Apache, а WP Rocket отбрасывает рекламный параметр и отвечает тем же файлом кеша
Server · кеш
gclid и кеш: запрос из клика по рекламе, ключ кеша страницы и замер первого байта
Замер по времени до первого байта: как запрос gclid в конце адреса из клика по рекламе игнорируется и ответ отдаётся из файла кеша адреса без запроса.
Читать →Текущая статья

ℹ️ О партнёрских ссылках
Материалы этого сайта содержат партнёрские ссылки. Если посетитель покупает товар или услугу по такой ссылке, сайт получает комиссию от продавца. При этом сумма, которую платит покупатель (при акционных скидках сумма снижается ↓), не увеличивается. Указанные цены, скидки и наличие актуальны на момент написания и могут отличаться, поэтому перед покупкой уточняйте их у продавца. Товары выбираются и оцениваются по нашим собственным критериям, а комиссия не влияет ни на порядок, ни на содержание обзоров.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Докажите, что вы человек: 2   +   8   =