WP Rocket mehrsprachiger Cache: Warum der Cache der übersetzten Seiten beim Speichern eines Entwurfs verschwindet und die Lösung

Der mehrsprachige Cache von WP Rocket ist ein Seiten-Cache, der für jede übersetzte TranslatePress-Adresse getrennt in einem Sprachordner liegt. Auf einem WordPress-Server mit Nginx + Apache habe ich im Code von WP Rocket verfolgt, warum der Cache der übersetzten Seiten bei jedem Speichern eines Entwurfs samt Sprachordner verschwand, und das Problem mit Code gelöst, der unveröffentlichte Beiträge vom Löschen des übersetzten Caches ausnimmt und selbst gelöschte Adressen erneut an den Preload meldet.
Die Zeit bis zum ersten Byte, gemessen auf demselben Server, lag mit dem Cache der übersetzten Seite bei 0,015 Sekunden und ohne ihn bei 2,492 Sekunden. Ist der Sprachordner leer, bekommen Besucher der übersetzten Seiten diese langsame Antwort, bis jemand die Adresse erneut aufruft und der Cache neu entsteht.
1️⃣ Aufbau des mehrsprachigen Caches von WP Rocket: Ordner je Sprache und Löschen der übersetzten Seiten
Eine übersetzte Seite zeigt den Originalbeitrag in Übersetzung, daher liegt die Annahme nahe, ihr Cache gehöre mit dem Original zusammen – doch WP Rocket cacht übersetzte Adressen als eigene Seiten. Im Cache-Ordner entstehen neben den Ordnern der Originalbeiträge Sprachordner wie [en] und [ja], und übersetzte Beiträge werden darin unter ihrem übersetzten Slug gespeichert.
Wenn du einen Beitrag bearbeitest, löscht die TranslatePress-Integration von WP Rocket auch den Cache seiner übersetzten Seiten. Auch die offizielle Dokumentation erklärt, dass eine normale Bearbeitung alle übersetzten Versionen löscht, eine Änderung im Übersetzungseditor von TranslatePress dagegen nur die geänderte Sprache.
// wp-rocket/inc/ThirdParty/Plugins/I18n/TranslatePress.php (3.23.5.1)
'post_updated' => 'clear_post_languages', // läuft bei jedem Speichern eines Beitrags
public function clear_post_languages( $post_id ) {
// …
$default_permalink = get_permalink( $post_id ); // Beitragsstatus wird nicht geprüft
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 );
}Die Integrationsfunktion wandelt die Beitragsadresse für jede Sprache in die übersetzte Adresse um und übergibt sie dann an die Funktion zum Aufräumen der Cache-Dateien, [rocket_clean_files]. Dabei prüft keine Zeile den Beitragsstatus – übersetzte Adressen werden also bei veröffentlichten Beiträgen wie bei Entwürfen gleich erzeugt und gelöscht.
2️⃣ Entwurf speichern: der ganze Sprachordner wird gelöscht
Das Speichern eines Entwurfs hat sehr wohl mit dem Cache der übersetzten Seiten zu tun. Vor der Veröffentlichung ist der Permalink eines Beitrags kein Slug, sondern hat die Form [?p=Beitrags-ID], und TranslatePress wandelt diese Adresse in eine mit Sprachpfad um, etwa [/en/?p=Beitrags-ID].
[rocket_clean_files] entfernt die Query aus der Adresse und sucht den Cache-Speicherort nur über den Pfad. Bleibt [/en/] übrig, ist das der gesamte Cache-Ordner dieser Sprache, und ist das Ziel ein Ordner, wird er samt Unterordnern rekursiv gelöscht.
// wp-rocket/inc/functions/files.php (3.23.5.1) — in rocket_clean_files()
$parsed_url = get_rocket_parse_url( $url ); // trennt Pfad und Query
// …
$entry = $dir . $parsed_url['path']; // …/example.com/en/
// …
if ( $filesystem->is_dir( $entry ) ) {
rocket_rrmdir( $entry, [], $filesystem ); // löscht den ganzen Ordner rekursiv
} else {
$filesystem->delete( $entry );
}Die Startseite läuft über denselben Weg. Ihre Adresse [/] wird zu [/en/], daher löscht schon das Speichern der veröffentlichten Startseite den gesamten Sprachordner. Bereits das erneute Speichern eines Entwurfs mit nur geändertem Titel leerte die Cache-Ordner aller vier Sprachen, und anhand der Speicherzeiten und Cache-Löschprotokolle habe ich bestätigt, dass sich dieselbe Löschung in einer Nacht mit vielen Entwurfsspeicherungen über 30-mal wiederholte.
Laut Code nimmt auch das automatische Speichern im Editor denselben Weg. Revisionen, die beim Bearbeiten eines veröffentlichten Beitrags gespeichert werden, nutzen ebenfalls eine Adresse der Form [?p=].
Auf einer Testseite mit Nginx + Apache habe ich einen veröffentlichten Beitrag im Editor bearbeitet und dabei die Dateien in den Sprachordnern gezählt. Nach dem ersten automatischen Speichern hatten der englische und der japanische Ordner noch je 20 Dateien, nach dem zweiten eine Minute später waren beide Ordner bei 0.
Das erste automatische Speichern legt eine neue Revision an, ab dem zweiten wird dieselbe Revision geändert und gespeichert – dabei läuft [post_updated]. Bearbeitest du nach dem ersten automatischen Speichern weiter, wird der Cache der übersetzten Seiten samt Sprachordner gelöscht, auch ohne dass du auf Speichern klickst.
Im offiziellen Repository von WP Rocket ist dasselbe Symptom seit dem 31. August 2023 als Issue #6130 offen. Der Pull Request #7616 mit einer Korrektur kam im September 2025, wurde aber nicht gemergt, und auch das Changelog von 3.23.5.1 vom 2. Oktober 2026 enthält keine Korrektur.
3️⃣ Lösungscode: Cache der übersetzten Seiten nur bei veröffentlichten Beiträgen löschen
Bis eine offizielle Korrektur kommt, löst man die Integrationsfunktion und ersetzt sie durch eine Funktion, die übersetzte Seiten nur bei eindeutiger Adresse löscht. Der in den Issue-Kommentaren gepostete Workaround geht in dieselbe Richtung: Er verlässt die Integrationsfunktion sofort, wenn der Beitrag nicht veröffentlicht ist.
Eine Bedingung, die nur den Beitragsstatus prüft, lässt jedoch zwei Fälle offen. Die Startseite löscht beim Speichern den ganzen Sprachordner, obwohl sie veröffentlicht ist, und setzt du einen veröffentlichten Beitrag zurück auf Entwurf, wird der Cache seiner übersetzten Seiten nicht gelöscht – der zurückgezogene Beitrag lässt sich unter den übersetzten Adressen weiter aufrufen.
<?php
/**
* Plugin Name: Rocket TranslatePress Draft Fix
* Description: Beschränkt das Löschen des Caches übersetzter Seiten durch die WP Rocket TranslatePress-Integration auf Fälle mit eindeutiger Adresse
*/
// 1. WP Rocket Integrationsfunktion von post_updated lösen und aufbewahren
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. Ersatzfunktion
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'] ] );
// Startseite – nur die Cache-Dateien der Sprach-Startseite, nicht der Sprachordner
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 ); // Preload benachrichtigen (5️⃣)
return;
}
// Veröffentlichten Beitrag speichern – ursprüngliche WP Rocket Funktion unverändert
if ( 'publish' === $after->post_status ) {
call_user_func( $GLOBALS['rtdf_original'], $post_id );
return;
}
// Veröffentlicht → Entwurf·privat·Papierkorb – nur die übersetzten Seiten der Adresse vor dem Zurückziehen
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 );
}
// Alles andere (Entwürfe·Revisionen·Autosave) hat keine übersetzte Adresse, daher wird nichts gelöscht
}, 10, 3 );Der erste Teil sucht die von WP Rocket registrierte Integrationsfunktion, löst sie von [post_updated] und bewahrt die ursprüngliche Funktion auf. Die Ersatzfunktion löscht bei der Startseite nur die Cache-Dateien der Sprach-Startseiten, führt bei veröffentlichten Beiträgen die aufbewahrte Originalfunktion unverändert aus und sucht und löscht bei einem zurückgezogenen Beitrag die übersetzten Seiten über die Adresse von vor dem Zurückziehen.
Als PHP-Datei im Ordner [wp-content/mu-plugins/] gespeichert, greift er sofort ohne Aktivierung, und löscht man die Datei, verhält sich WP Rocket wieder wie ursprünglich. Im Test mit Code, der sich genauso verhält, blieb die Dateizahl in den Sprachordnern beim erneuten Speichern eines Entwurfs gleich, und beim erneuten Speichern eines veröffentlichten Beitrags wurden nur dessen Original- und Übersetzungsordner gelöscht.
Vor und nach dem Einfügen des Codes oben habe ich in derselben Umgebung die Dateizahl der Sprachordner für vier Fälle verglichen. Der englische und der japanische Ordner enthielten je 20 PC- und Mobil-Cache-Dateien der Sprach-Startseite und von vier Beiträgen.
Vor dem Einfügen des Codes fielen beide Ordner beim Speichern eines Entwurfs, beim Speichern der Startseite und beim Zurückziehen der Veröffentlichung auf 0; nur das Speichern eines veröffentlichten Beitrags löschte allein den Cache seiner übersetzten Seiten.
Nach dem Einfügen blieben beim Speichern eines Entwurfs und beim automatischen Speichern im Editor alle 20 Dateien erhalten. Das Speichern der Startseite löschte die 4 Cache-Dateien der Sprach-Startseiten, das Speichern eines veröffentlichten Beitrags und das Zurückziehen nur die 4 übersetzten Dateien dieses Beitrags, sodass 16 übrig blieben; die englische übersetzte Adresse des zurückgezogenen Beitrags lieferte 404.
| Fall | Vor der Korrektur | Nach dem Einfügen des Codes | Danach gelöschter Cache |
|---|---|---|---|
| Entwurf speichern | 20 → 0 | 20 → 20 | Nein |
| Veröffentlichten Beitrag speichern | Nur die übersetzten Seiten dieses Beitrags gelöscht | 20 → 16 | 4 übersetzte Dateien dieses Beitrags |
| Startseite speichern | 20 → 0 | 20 → 16 | 4 Cache-Dateien der Sprach-Startseiten |
| Veröffentlichung zurückziehen | 20 → 0 | 20 → 16 | 4 übersetzte Dateien dieses Beitrags · übersetzte Adresse liefert 404 |
4️⃣ Preload baut nicht neu: Status abgeschlossen bleibt in der Warteschlange
Auch wenn das Speichern von Entwürfen abgefangen ist: Cache-Dateien, die ohne das Lösch-Signal einer WP Rocket Funktion gelöscht wurden, füllt der Preload nicht wieder auf. Der Preload von WP Rocket nutzt eine Warteschlangen-Tabelle, die für jede Adresse einen Status speichert: Anzufragende Adressen sind ausstehend, angefragte in Bearbeitung, Adressen mit fertigem Cache abgeschlossen, und was nicht innerhalb von 3 Minuten fertig wird, gilt als fehlgeschlagen.
Eine abgeschlossene Adresse geht in drei Fällen zurück auf ausstehend: Die Datei wurde nach Ablauf der Cache-Lebensdauer gelöscht, WP Rocket hat beim Löschen ein Lösch-Signal gesendet, oder der Fehlschlag liegt über 12 Stunden zurück. Übergibst du [rocket_clean_files] als drittes Argument false oder löschst Dateien direkt, kommt dieses Signal nicht – in der Warteschlange bleibt die Adresse abgeschlossen, nur die Cache-Dateien verschwinden.
Code, der auf das Lösch-Signal hin weitere Caches löscht, übergibt beim dritten Argument false, um wiederholte Aufrufe zu verhindern. Beim Abgleich der übersetzten Caches waren von 65 englischen Seiten 3 und von 65 japanischen 7 in der Warteschlange noch abgeschlossen, hatten aber keine Cache-Dateien mehr.
Auch ein Mitwirkender aus dem Support-Team von WP Rocket hat in den Kommentaren zu Issue #6130 eingeräumt, dass Adressen in der Tabelle nach dem Löschen des Caches abgeschlossen bleiben und der Status auseinanderläuft. Der Vorschlag, Tabelle und Cache-Dateien regelmäßig abzugleichen (#2939), wurde 2020 ohne Umsetzung geschlossen.
5️⃣ Erneut für den Preload anmelden: selbst gelöschte Adressen der Warteschlange melden
Für selbst gelöschte Adressen genügt eine Zeile, die den Preload direkt nach dem Löschen benachrichtigt. Empfängt der Preload das Signal [rocket_after_clean_terms], setzt er die übergebenen Adressen zurück auf ausstehend und nimmt fehlende Adressen neu in die Tabelle auf.
// Nach dem Löschen ohne Lösch-Signal – Array der gelöschten Adressen erneut in der Preload-Warteschlange anmelden
rocket_clean_files( $urls, null, false ); // drittes Argument false = kein Lösch-Signal
do_action( 'rocket_after_clean_terms', $urls ); // der Preload setzt diese Adressen auf wartend (pending)Innerhalb von WP Rocket empfangen dieses Signal nur die Funktion der Preload-Warteschlange und die Hosting-Integration für SpinUpWP; die Preload-Funktion ändert nur Zeilen der Warteschlange, löscht keine WP Rocket Cache-Dateien und sendet kein erneutes Lösch-Signal – wiederholte Aufrufe entstehen also nicht. Websites, die den Cache nur über WP Rocket Funktionen löschen und das dritte Argument auf dem Standardwert lassen, senden das Signal bereits und brauchen diese Zeile nicht.
Nach dem Einfügen dieser Zeile habe ich den Cache eines Beitrags mit übersetzten Seiten in vier Sprachen gelöscht – innerhalb von 2 Sekunden standen seine vier Zeilen in der Warteschlange auf ausstehend. In zwei Tests auf einem Server, der den Cron jede Minute ausführt, waren alle 8 PC- und Mobil-Cache-Dateien der übersetzten Seiten nach 101 bzw. 153 Sekunden wieder da. Prüfen lässt sich das über den Status in der Warteschlangen-Tabelle und die Dateien im Cache-Ordner.
# Status dieser Adresse in der Warteschlangen-Tabelle (wp_ vor dem Tabellennamen ist das Präfix der Website)
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# Ob die Cache-Dateien etwa eine Minute später wieder da sind
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_gzipDirekt nach dem Löschen wechselt der Status auf [pending], und sobald der Preload die Anfrage geschickt hat und die Cache-Datei entstanden ist, auf [completed]. Steht der Status auf [completed], fehlt aber die Cache-Datei, wurde diese Adresse ohne Signal gelöscht.
Als ich die Startseite speicherte und der Code oben die Cache-Dateien der Sprach-Startseiten löschte und das Signal sendete, kamen die englische und die japanische Startseiten-Adresse wieder in die Warteschlange. Der PC- und Mobil-Cache der Sprach-Startseiten war spätestens beim zweiten Cron-Lauf wieder da.
In zwei Tests vergingen vom Speichern bis zur letzten Cache-Datei 47 bzw. 91 Sekunden.
🔢 FAQ & empfohlene Inhalte
















ℹ️ Affiliate-Hinweis
Die Inhalte dieser Website enthalten Affiliate-Links. Kauft ein Besucher über einen dieser Links ein Produkt oder eine Dienstleistung, erhält die Website vom Verkäufer eine Provision. Der Betrag, den der Käufer zahlt(bei Aktionsrabatten sinkt er ↓), steigt dadurch nicht. Angegebene Preise, Rabatte und Lagerbestände entsprechen dem Stand bei Erstellung und können abweichen; prüfe sie daher vor dem Kauf beim Verkäufer. Produkte werden nach eigenen Kriterien ausgewählt und bewertet, und Provisionen beeinflussen weder die Reihenfolge noch den Inhalt der Bewertungen.