Cache multilingue de WP Rocket : pourquoi le cache des versions traduites disparaît à l'enregistrement d'un brouillon, et la solution

Le cache multilingue de WP Rocket est un cache de page stocké à part, dans un dossier de langue, pour chaque adresse traduite par TranslatePress. Sur un serveur WordPress en Nginx + Apache, nous avons remonté dans le code de WP Rocket la cause de la disparition du cache des versions traduites, avec tout leur dossier de langue, à chaque enregistrement d'un brouillon, puis nous l'avons réglée avec un code qui exclut les articles non publiés de la suppression du cache traduit et signale de nouveau au préchargement les adresses supprimées à la main.
Le temps jusqu'au premier octet, mesuré sur le même serveur, était de 0,015 seconde avec le cache de la version traduite et de 2,492 secondes sans. Quand le dossier de langue est vide, les visiteurs des versions traduites reçoivent cette réponse lente jusqu'à ce que quelqu'un rouvre l'adresse et que le cache soit recréé.
1️⃣ Structure du cache multilingue de WP Rocket : dossiers par langue et suppression des versions traduites
Une version traduite affiche l'article d'origine traduit : on pense donc volontiers que son cache va avec celui de l'original, mais WP Rocket met en cache les adresses traduites comme des pages distinctes. Dans le dossier de cache apparaissent, à côté des dossiers des articles d'origine, des dossiers de langue comme [en] et [ja], et les articles traduits y sont stockés sous le nom de leur slug traduit.
Quand vous modifiez un article, l'intégration TranslatePress de WP Rocket supprime aussi le cache de ses versions traduites. La documentation officielle précise elle aussi qu'une modification normale efface toutes les versions traduites, alors qu'une correction faite dans l'éditeur de traduction de TranslatePress n'efface que la langue modifiée.
// wp-rocket/inc/ThirdParty/Plugins/I18n/TranslatePress.php (3.23.5.1)
'post_updated' => 'clear_post_languages', // s'exécute à chaque enregistrement d'un article
public function clear_post_languages( $post_id ) {
// …
$default_permalink = get_permalink( $post_id ); // le statut de l'article n'est pas vérifié
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 );
}La fonction d'intégration convertit l'adresse de l'article en adresse traduite pour chaque langue, puis la transmet à la fonction de nettoyage des fichiers de cache, [rocket_clean_files]. Aucune ligne de ce processus ne vérifie le statut de l'article : les adresses traduites sont donc créées puis supprimées de la même façon, que l'article soit publié ou en brouillon.
2️⃣ Enregistrer un brouillon : tout le dossier de langue est supprimé
L'enregistrement d'un brouillon n'est pas sans lien avec le cache des versions traduites. Avant publication, le permalien d'un article n'est pas un slug mais de la forme [?p=ID de l'article], et TranslatePress convertit cette adresse en une adresse avec chemin de langue, comme [/en/?p=ID de l'article].
[rocket_clean_files] retire la requête de l'adresse et cherche l'emplacement du cache uniquement par le chemin. Si le chemin restant est [/en/], c'est tout le dossier de cache de cette langue, et si la cible est un dossier, elle est supprimée récursivement, sous-dossiers compris.
// wp-rocket/inc/functions/files.php (3.23.5.1) — dans rocket_clean_files()
$parsed_url = get_rocket_parse_url( $url ); // sépare le chemin et la requête
// …
$entry = $dir . $parsed_url['path']; // …/example.com/en/
// …
if ( $filesystem->is_dir( $entry ) ) {
rocket_rrmdir( $entry, [], $filesystem ); // supprime tout le dossier récursivement
} else {
$filesystem->delete( $entry );
}La page d'accueil suit le même chemin. Son adresse [/] est convertie en [/en/], si bien que même l'enregistrement de la page d'accueil publiée supprime tout le dossier de langue. Rien qu'en modifiant le titre d'un brouillon puis en l'enregistrant de nouveau, les dossiers de cache des quatre langues se sont vidés, et les heures d'enregistrement et les journaux de suppression du cache nous ont montré la même suppression répétée plus de 30 fois en une nuit chargée en enregistrements de brouillons.
Dans le code, l'enregistrement automatique de l'éditeur suit le même chemin. Les révisions enregistrées pendant la modification d'un article publié utilisent elles aussi une adresse de la forme [?p=].
Sur un site de test en Nginx + Apache, nous avons modifié un article publié dans l'éditeur en comptant les fichiers des dossiers de langue. Après le premier enregistrement automatique, les dossiers anglais et japonais avaient toujours 20 fichiers chacun ; après le second, une minute plus tard, les deux étaient à 0.
Le premier enregistrement automatique crée une nouvelle révision ; à partir du deuxième, c'est la même révision qui est modifiée et enregistrée, et [post_updated] se déclenche. Si vous continuez à modifier le contenu après le premier enregistrement automatique, le cache des versions traduites est supprimé avec tout le dossier de langue, même sans cliquer sur le bouton d'enregistrement.
Le même symptôme est ouvert dans le dépôt officiel de WP Rocket sous l'issue #6130 depuis le 31 août 2023. La pull request #7616 qui le corrige a été proposée en septembre 2025 mais n'a pas été fusionnée, et le journal des modifications de la 3.23.5.1, publiée le 2 octobre 2026, ne contient aucune correction.
3️⃣ Code correctif : ne supprimer le cache des versions traduites que pour les articles publiés
En attendant un correctif officiel, on détache la fonction d'intégration pour la remplacer par une fonction qui ne supprime les versions traduites que lorsque l'adresse est certaine. Le contournement publié dans les commentaires de l'issue va dans le même sens : il sort immédiatement de la fonction d'intégration si l'article n'est pas publié.
Une condition qui ne vérifie que le statut de l'article laisse toutefois deux cas. La page d'accueil efface tout le dossier de langue à l'enregistrement même si elle est publiée, et quand un article publié repasse en brouillon, le cache de ses versions traduites n'est pas supprimé : l'article retiré reste accessible à ses adresses traduites.
<?php
/**
* Plugin Name: Rocket TranslatePress Draft Fix
* Description: Limite la suppression du cache des versions traduites par l'intégration TranslatePress de WP Rocket aux cas où l'adresse est certaine
*/
// 1. Détacher la fonction d'intégration de WP Rocket de post_updated et la conserver
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. Fonction de remplacement
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'] ] );
// Page d'accueil — uniquement les fichiers de cache de la page d'accueil de la langue, pas le dossier de langue
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 ); // prévenir le préchargement (5️⃣)
return;
}
// Enregistrement d'un article publié — fonction d'origine de WP Rocket telle quelle
if ( 'publish' === $after->post_status ) {
call_user_func( $GLOBALS['rtdf_original'], $post_id );
return;
}
// Publié → brouillon·privé·corbeille — uniquement les versions traduites de l'adresse d'avant le retrait
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 );
}
// Le reste (brouillons·révisions·enregistrement automatique) n'a pas d'adresse traduite, donc rien n'est supprimé
}, 10, 3 );La première partie repère la fonction d'intégration enregistrée par WP Rocket, la détache de [post_updated] et conserve la fonction d'origine. La fonction de remplacement ne supprime que les fichiers de cache des pages d'accueil de langue s'il s'agit de la page d'accueil, exécute telle quelle la fonction d'origine conservée pour un article publié et, quand un article publié est retiré, retrouve et supprime ses versions traduites à partir de l'adresse d'avant le retrait.
Enregistré comme fichier PHP dans le dossier [wp-content/mu-plugins/], il s'applique immédiatement sans activation, et supprimer le fichier rend à WP Rocket son comportement d'origine. Lors d'un test avec un code au comportement identique, réenregistrer un brouillon a laissé inchangé le nombre de fichiers des dossiers de langue, et réenregistrer un article publié n'a supprimé que les dossiers de cet article, original et versions traduites.
Avant et après l'ajout du code ci-dessus, nous avons comparé dans le même environnement le nombre de fichiers des dossiers de langue pour quatre cas. Les dossiers anglais et japonais contenaient chacun 20 fichiers de cache PC et mobile pour la page d'accueil de la langue et quatre articles.
Avant l'ajout du code, l'enregistrement d'un brouillon, l'enregistrement de la page d'accueil et la dépublication ont tous trois ramené les deux dossiers à 0, et seul l'enregistrement d'un article publié n'a supprimé que le cache de ses versions traduites.
Après l'ajout du code, les 20 fichiers sont restés lors de l'enregistrement d'un brouillon et de l'enregistrement automatique de l'éditeur. L'enregistrement de la page d'accueil a supprimé les 4 fichiers de cache des pages d'accueil de langue, et l'enregistrement d'un article publié ou sa dépublication n'ont supprimé que ses 4 fichiers traduits, laissant 16 fichiers ; l'adresse anglaise de l'article retiré a renvoyé une erreur 404.
| Cas | Avant le correctif | Après l'ajout du code | Cache supprimé après |
|---|---|---|---|
| Enregistrement d'un brouillon | 20 → 0 | 20 → 20 | Non |
| Enregistrement d'un article publié | Seules les versions traduites de cet article sont supprimées | 20 → 16 | 4 fichiers traduits de cet article |
| Enregistrement de la page d'accueil | 20 → 0 | 20 → 16 | 4 fichiers de cache des pages d'accueil de langue |
| Dépublication | 20 → 0 | 20 → 16 | 4 fichiers traduits de cet article · l'adresse traduite renvoie 404 |
4️⃣ Préchargement non régénéré : statut terminé resté dans la file d'attente
Même avec l'enregistrement des brouillons neutralisé, les fichiers de cache supprimés sans le signal de suppression d'une fonction de WP Rocket ne sont pas recréés par le préchargement. Le préchargement de WP Rocket utilise une table de file d'attente qui enregistre un statut pour chaque adresse : les adresses à demander sont en attente, celles demandées sont en cours, celles dont le cache est créé passent à terminé, et celles qui ne finissent pas en 3 minutes passent en échec.
Une adresse terminée repasse en attente dans trois cas : fichier supprimé à l'expiration du cache, adresse supprimée par WP Rocket avec envoi du signal de suppression, et adresse en échec depuis plus de 12 heures. Si vous passez false comme troisième argument de [rocket_clean_files] ou supprimez les fichiers directement, ce signal n'est pas envoyé : seuls les fichiers de cache disparaissent, tandis que la file d'attente les garde comme terminés.
Le code qui reçoit le signal de suppression puis efface d'autres caches passe false comme troisième argument pour éviter les appels en boucle. Au moment du rapprochement des caches traduits, 3 des 65 versions anglaises et 7 des 65 versions japonaises restaient terminées dans la file d'attente alors que leurs fichiers de cache avaient disparu.
Un contributeur du support de WP Rocket a lui aussi reconnu dans les commentaires de l'issue #6130 qu'après la suppression du cache, les adresses de la table restent terminées et que l'état se désynchronise. La proposition de resynchroniser régulièrement la table et les fichiers de cache (#2939) a été fermée en 2020 sans avoir été mise en œuvre.
5️⃣ Réinscription au préchargement : signaler à la file d'attente les adresses supprimées soi-même
Pour les adresses supprimées soi-même, une ligne qui prévient le préchargement juste après la suppression suffit. Quand le préchargement reçoit le signal [rocket_after_clean_terms], il remet en attente les adresses reçues et ajoute celles qui manquent dans la table.
// Après une suppression sans le signal — réinscrire le tableau des adresses supprimées dans la file d'attente du préchargement
rocket_clean_files( $urls, null, false ); // troisième argument false = pas de signal de suppression
do_action( 'rocket_after_clean_terms', $urls ); // le préchargement remet ces adresses en attente (pending)Dans WP Rocket, seuls la fonction de file d'attente du préchargement et l'intégration d'hébergement SpinUpWP reçoivent ce signal, et la fonction de préchargement se contente de mettre à jour les lignes de la file sans supprimer de fichiers de cache WP Rocket ni renvoyer le signal de suppression : il n'y a donc pas d'appels en boucle. Les sites qui vident le cache uniquement avec les fonctions de WP Rocket et laissent le troisième argument à sa valeur par défaut envoient déjà le signal et n'ont pas besoin de cette ligne.
Après l'ajout de cette ligne, nous avons vidé le cache d'un article traduit en quatre langues : en moins de 2 secondes, ses quatre lignes de file d'attente sont passées en attente. Lors de deux tests sur un serveur qui exécute le cron chaque minute, les 8 fichiers de cache PC et mobile des versions traduites ont tous été recréés au bout de 101 et 153 secondes. On le vérifie avec le statut dans la table de la file d'attente et les fichiers du dossier de cache.
# Statut de cette adresse dans la table de la file d'attente (le wp_ devant le nom de la table est le préfixe du site)
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# Si les fichiers de cache sont recréés environ une minute plus tard
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_gzipJuste après la suppression, le statut passe à [pending], puis à [completed] une fois que le préchargement a envoyé sa requête et que le fichier de cache est créé. Si le statut est [completed] mais qu'il n'y a pas de fichier de cache, cette adresse a été supprimée sans le signal.
Quand nous avons enregistré la page d'accueil et que le code ci-dessus a supprimé les fichiers de cache des pages d'accueil de langue en envoyant le signal, les adresses d'accueil anglaise et japonaise sont revenues dans la file d'attente. Les caches PC et mobile des pages d'accueil de langue ont été recréés au plus tard à la deuxième exécution du cron.
Lors de deux tests, il a fallu 47 et 91 secondes entre l'enregistrement et le dernier fichier de cache.
🔢 FAQ & contenus recommandés
















ℹ️ Mention d'affiliation
Le contenu de ce site contient des liens d'affiliation. Lorsqu'un visiteur achète un produit ou un service via l'un de ces liens, le site perçoit une commission du vendeur. Le montant payé par l'acheteur(il baisse lors des promotions ↓), lui, n'augmente pas. Les prix, remises et stocks indiqués correspondent à la date de rédaction et peuvent différer ; vérifiez-les donc auprès du vendeur avant d'acheter. Les produits sont choisis et évalués selon nos propres critères, et les commissions n'influencent ni l'ordre de présentation ni le contenu des évaluations.