Zum Inhalt springen
/ Home / Knowledge / Schnell ausgeliefert, trotzdem langsam: REST-API-Reads richtig cachen (mit Auto-Purge beim Speichern)

Schnell ausgeliefert, trotzdem langsam: REST-API-Reads richtig cachen (mit Auto-Purge beim Speichern)

Marius Marzian
SEO-Experte
OIT GmbH
Profil ansehen

Eine Seite kann blitzschnell ausgeliefert werden und sich trotzdem zäh anfühlen. Genau dieses Paradox begegnet uns regelmäßig bei modernen WordPress-Sites – und die Ursache ist fast immer dieselbe: dynamische Inhalte werden per JavaScript aus einem ungecachten API-Endpoint nachgeladen. Dieser Beitrag zeigt, woran man das erkennt und wie ein kleiner Micro-Cache mit einem besonders eleganten Invalidierungs-Trick das Problem löst.

Das Paradox: HTML in 0,16 s, gefühlt aber 5 Sekunden

Ein typischer Befund aus der Praxis: Das HTML einer Seite kommt aus dem Page-Cache (Varnish/LiteSpeed) mit einem TTFB von ~0,16 Sekunden – traumhaft. Trotzdem berichten Besucher, die Seite „ruckelt“ oder „baut sich langsam auf“.

Der Grund: Das ausgelieferte HTML enthält die dynamischen Bereiche (z. B. Team-Fotos, Blog-Teaser, Logo-Slider) noch gar nicht – dort stehen leere Container. Erst im Browser fragt JavaScript per fetch() die Daten nach:

fetch('/wp-json/wp/v2/team_member?per_page=30&_fields=id,title,link')
  .then(r => r.json())
  .then(renderGrid);

Und genau diese Nachfrage ist der Flaschenhals.

Der blinde Fleck: /wp-json/ ist vom Cache ausgenommen

Page-Caches liefern HTML-Seiten aus dem Speicher – die REST-API dagegen wird fast überall bewusst vom Caching ausgeschlossen, weil sie auch für Schreibvorgänge und personalisierte Daten zuständig ist. Im Response-Header sieht das so aus:

x-cacheable: NO:URL:WP-JSON
x-varnish-cache: BYPASS
cache-control: no-store, no-cache, must-revalidate

Heißt: Jeder einzelne Seitenaufruf löst eine frische, ungecachte Datenbankabfrage im WordPress-Backend aus. Bei verschachtelten Abfragen, vielen aktiven Plugins oder einem mehrsprachigen Setup dauert das schnell 3 bis 8 Sekunden. Die HTML-Seite ist längst da – aber die Fotos und Cards „poppen“ erst Sekunden später rein. Das ist die gefühlte Langsamkeit.

Die Lösung: ein Micro-Cache vor den Lese-Endpoints

Lese-Endpoints (GET) für öffentliche Inhalte müssen nicht in Echtzeit sein. Ein kurzer serverseitiger Cache vor diesen Routen senkt die Antwortzeit von mehreren Sekunden auf ~50 Millisekunden – ohne eine einzige Zeile im Frontend zu ändern. Das vorhandene fetch() bleibt, es bekommt seine Daten nur sofort.

In WordPress lässt sich das sauber an zwei Filtern einklinken:

  • rest_pre_dispatch – fängt den Request vor der teuren Query ab und liefert bei einem Cache-Treffer direkt die gespeicherte Antwort.
  • rest_pre_echo_response – greift die fertige Payload ab (inklusive _fields-Filter und _embed) und legt sie bei einem Cache-Miss ab.

Wichtig dabei: Nur anonyme GET-Requests werden gecacht. Eingeloggte Redakteure und der Gutenberg-Editor bekommen immer frische Daten – sonst würde man im Backend veraltete Inhalte sehen.

Das elegante Stück: Auto-Purge per „Versions-Bump“

Die spannendste Frage bei jedem Cache lautet: Wann wird er ungültig? Ein fester Timer (z. B. 10 Minuten) ist ein Sicherheitsnetz – aber er bedeutet auch, dass ein frisch veröffentlichter Blogbeitrag bis zu 10 Minuten unsichtbar bleibt.

Viel schöner ist ereignisgesteuerte Invalidierung beim Speichern. Der klassische Weg – beim Speichern jeden betroffenen Cache-Key einzeln suchen und löschen – ist fehleranfällig, weil die Keys von Query-Parametern abhängen (per_page, _fields, page …).

Der Trick: Man baut eine Cache-Versionsnummer in jeden Key ein und erhöht beim Speichern einfach diese eine Nummer. Damit zeigen alle alten Keys ins Leere – der gesamte Cache ist in einer einzigen, atomaren Operation entwertet, ohne dass man auch nur einen einzelnen Eintrag kennen muss:

// Versionsnummer in den Key einweben
function cache_key($request) {
    $ver = (int) get_option('restc_ver', 1);
    $params = $request->get_query_params();
    ksort($params);
    return 'restc_' . $ver . '_' . md5($request->get_route() . wp_json_encode($params));
}

// Beim Speichern: Version hochzählen → ALLE Keys sofort ungültig
add_action('save_post', function () {
    update_option('restc_ver', ((int) get_option('restc_ver', 1)) + 1);
});

Die alten Transients verschwinden danach von selbst, sobald ihr Timer abläuft. Das Ergebnis: Inhalte sind beim Speichern sofort aktuell, und der Backend wird trotzdem entlastet. Dasselbe Muster („generation counter“ oder „cache busting via version“) nutzen auch große CDNs und Frameworks – es ist robust, lock-frei und braucht keine Liste aller Keys.

Wann lieber Server-Side Rendering (SSR)?

Caching behebt das Tempo, aber der Inhalt wird weiterhin per JavaScript nachgeladen. Für die meisten Widgets (Slider, Team-Grid, Teaser) ist das völlig ausreichend. In zwei Fällen lohnt der Schritt zum echten Server-Side-Rendering, bei dem der Inhalt direkt ins HTML gerendert wird:

  • SEO: Soll der Inhalt zuverlässig indexiert werden, gehört er ins initiale HTML – nicht erst per JS nachgeladen.
  • Kein „Pop-in“: Wenn schon der erste Frame perfekt sitzen soll (Layout-Stabilität, Core Web Vitals / CLS), rendert man serverseitig vor.

Die Praxis-Faustregel: Caching zuerst – es bringt 95 % des Tempogewinns bei minimalem Risiko und ohne Eingriff in bestehende, handgetunte Komponenten. SSR ist die Kür für die Fälle, in denen es wirklich darauf ankommt.

Die wichtigsten Erkenntnisse

  • Ein guter Page-Cache (niedriger TTFB) sagt nichts über die gefühlte Geschwindigkeit aus, wenn Inhalte per JS aus ungecachten APIs nachgeladen werden.
  • /wp-json/ ist standardmäßig vom Caching ausgenommen – jeder Aufruf trifft das rohe Backend.
  • Ein Micro-Cache (10 Min) für anonyme GET-Reads senkt 3–8 s auf ~50 ms – ohne Frontend-Änderung.
  • Cache-Invalidierung per Versions-Bump beim Speichern macht Inhalte sofort aktuell und ist deutlich robuster als das Löschen einzelner Keys.
  • SSR ist der nächste Schritt für SEO und maximale Layout-Stabilität – aber Caching kommt zuerst.

Fragen zu Performance-Optimierung, Core Web Vitals oder einer trägen WordPress-Site? Das Team von OIT hilft gerne weiter.

Marius Marzian
Dein Ansprechpartner
OIT GmbH
Profil ansehen

Hast du eine Frage zu diesem Artikel?

Schreib uns – wir antworten in der Regel innerhalb eines Werktages.