Shopware 6.8 cached (fast) alles. Auch Dinge, die du nie cachen wolltest.

Terminal: curl-Age-Header zeigt Cache-HIT auch für eingeloggte Kunden — 0,26 s statt 0,85 s

Mit Shopware 6.8 wird ein Umbau zum Standard, der seit Version 6.7.5 als Feature-Flag CACHE_REWORK im Kern schlummert: das HTTP-Cache-Rework. Die Richtung ist eindeutig — der Cache wird gierig. Er behält Seiten länger, er bedient Nutzergruppen, die bisher immer am Cache vorbeiliefen, und er greift sich Schichten, die nie gecacht waren. Für die Ladezeit ist das ein Geschenk. Für jeden, der Plugins baut oder betreibt, ist es eine Prüfungsfrage. Wir haben beides gemessen.

Was sich ändert — die vier Umbauten

1. Der Cache-Schlüssel wird radikal entrümpelt. Bisher floss allerlei Kontext in die Entscheidung ein, welche Cache-Variante ein Besucher bekommt — inklusive der Rule-Engine, deren Regeln ganze Besuchergruppen am Cache vorbeischleusten. Künftig zählen im Wesentlichen nur noch: Sales Channel, Sprache, Währung, Steuer-Zustand, Kundengruppe. Weniger Varianten heißt: Jede Seite wird schneller warm und bleibt es — die Cache-Quote steigt strukturell.

2. Eingeloggte Kunden surfen aus dem Cache. Bis heute gilt: Wer eingeloggt ist, bekommt jede Seite frisch aus dem Backend gerendert. Künftig bekommen angemeldete Besucher dieselben gecachten Seiten wie anonyme — alles Persönliche (Preise, Verfügbarkeiten, der Name im Header) wird per AJAX oder ESI nachgeladen.

3. Auch die Store-API wird HTTP-gecacht. Headless-Frontends und Apps, deren Anfragen bisher grundsätzlich durchs Backend liefen, bekommen eine Cache-Schicht davor.

4. Invalidieren heißt nicht mehr Wegwerfen. Statt Cache-Einträge hart zu löschen, markiert ein Soft-Purge sie als veraltet; die Auffrischung passiert im Hintergrund, ohne dass ein Besucher auf den kalten Cache trifft.

Die Messreihe: zwei identische Shops, ein Flag Unterschied

Behauptungen kann jeder. Wir haben zwei identische Shopware-6.7.12-Instanzen auf derselben Maschine aufgesetzt, per Admin-API mit 200 identischen Laborprodukten befüllt und den Cache-Status jeder Antwort über den Age-Header protokolliert (Age-Header-Methode). Einziger Unterschied: Instanz B fährt mit CACHE_REWORK=1 das 6.8-Verhalten.

Der Grundstock, auf beiden gleich: Produktseite kalt 0,7–3,9 s, Startseite kalt bis 8,5 s — warm konstant 0,26 s. Der Cache ist der Unterschied zwischen „geht so“ und „sofort da“.

Der Money-Shot ist das Einloggen. Wir haben auf beiden Instanzen denselben Testkunden angelegt und dieselbe Produktseite abgerufen:

  • Heutiges Verhalten (6.7): Eingeloggt ist jede Anfrage ein Cache-Miss — 0,84 bis 0,92 Sekunden, voller Backend-Render, bei jedem einzelnen Klick. Der anonyme Kontroll-Abruf parallel dazu: 0,26 s aus dem Cache.
  • 6.8-Verhalten: Der eingeloggte Kunde wird aus dem Cache bedient — 0,26 s, Age zählt sauber hoch.

Faktor drei pro Klick für jeden angemeldeten Besucher — und die Backend-Last dieser Nutzergruppe verschwindet fast vollständig. Wer einen B2B-Shop betreibt, dessen Kundschaft grundsätzlich eingeloggt bestellt, darf diese Zeile zweimal lesen: Bisher war die effektive Cache-Quote solcher Shops null.

Und das ist keine Randnotiz für Spezialfälle. In der Praxis liegt die Gesamt-Hit-Quote selbst gut getunter Produktiv-Shops mit treuer, eingeloggter Stammkundschaft um die 50 % — nicht, weil der Cache schlecht arbeitet, sondern weil die andere Hälfte des Traffics unter 6.7 strukturell gar nicht cachebar ist: eingeloggte Besucher, kontext- und regelgebundene Varianten. Storage vergrößern, TTLs drehen, Warming fahren — nichts davon hilft gegen eine Architektur-Decke. Genau diese Hälfte holt das Rework in den Cache. Es geht hier nicht um die Optimierung von 80 auf 90 %; es geht um Traffic, der den Cache noch nie gesehen hat.

Der Überraschungsbefund: 6.7 invalidiert längst verzögert

Wir wollten eigentlich zeigen, wie 6.7 bei jeder Produktänderung sofort Cache-Einträge wegwirft. Tut es nicht: delay_enabled: true ist schon in 6.7 Standard — Invalidierungen werden gesammelt (bei uns: in MySQL) und von einem Scheduled Task im Hintergrund abgearbeitet. Unsere 120-Request-Simulation mit laufenden Produkt-Updates ergab auf beiden Instanzen 100 % Hit-Quote.

Darin steckt eine Betriebsfalle, die man kennen muss: Der Abbau läuft über den Task-Worker. Läuft der nicht, wird nie invalidiert — der Shop zeigt geänderte Preise und Bestände erst, wenn die TTL abläuft. Ob die eigenen Scheduled Tasks laufen, sieht man in Sekunden (FroshTools macht überfällige Tasks zum Ampel-Check). Und der Soft-Purge des Reworks macht den Worker erst recht zur Pflicht-Infrastruktur: Ohne ihn frischt im Hintergrund nichts auf.

Die Kehrseite: „Dinge, die du nie cachen wolltest“

Jetzt zur zweiten Hälfte der Überschrift. Der gierige Cache ist nur so lange ein Geschenk, wie alles, was er speichert, wirklich für alle gleich ist. Genau das ist bei Plugins nicht garantiert:

  • Ein Plugin, das kundenspezifische Inhalte serverseitig in die Seite rendert — Preise nach Login, Bestandsampeln je Kundengruppe jenseits des Schlüssels, „Willkommen zurück, Frau Müller“ — schreibt diese Inhalte künftig in den gemeinsamen Cache. Der nächste Besucher sieht sie. Aus einem Feature wird ein Datenleck.
  • Ein Plugin, das sein Verhalten an Rule-Engine-Regeln hängt, die nicht mehr im Cache-Schlüssel stecken, zeigt schlicht falsche Inhalte — mal die Aktion, mal keine, je nachdem, wer die Seite warm gemacht hat.
  • Eigene Cookies, die bisher stillschweigend Cache-Varianten erzeugten, tun das nicht mehr von allein — sie müssen explizit unter shopware.cache.cookies registriert werden.

Die Konsequenz für alle, die Erweiterungen bauen: Personalisierung gehört ab jetzt in AJAX-Endpunkte oder ESI-Includes, nie in die gecachte Seite. Und getestet wird das nicht durch Draufschauen, sondern mit Szenarien: als Kunde A warmsurfen, als Kunde B abrufen, vergleichen.

Die Checkliste vor dem 6.8-Update

Für Shop-Betreiber:

  • Worker-Betrieb sicherstellen (Messenger-Consumer + Scheduled Tasks) — ohne ihn funktioniert weder Invalidierung noch Soft-Purge.
  • Cache-Quote vorher/nachher messen (Age-Header-Methode) — der Effekt soll sichtbar sein, nicht gefühlt.
  • Jedes installierte Plugin fragen (oder fragen lassen): Rendert es kunden- oder regelspezifische Inhalte serverseitig? Wenn ja: vor dem Update klären, nicht nach der ersten Kundenbeschwerde.
  • Mit CACHE_REWORK=1 auf einer Staging-Umgebung vorziehen — das Flag existiert genau dafür.

Für Plugin-Hersteller: Personalisierung auf AJAX/ESI umstellen, eigene Cookies registrieren, Cache-Verhaltens-Tests in die Suite aufnehmen — und in der Dokumentation ehrlich sagen, was das Plugin vom Cache erwartet.

Was wir (noch) nicht gemessen haben

Transparenz gehört dazu: Das Soft-Purge-Verhalten und die Store-API-Quote haben wir noch nicht unter Last vermessen — dafür braucht das Labor einen dauerhaft laufenden Worker und echtes Traffic-Muster; das holen wir nach, sobald 6.8 in einer Release-Fassung vorliegt. Die Zahlen oben (Kalt/Warm, Eingeloggt, Invalidierungs-Verhalten) sind gemessen, nicht geschätzt — Rohdaten liegen bei uns im Repo.

Auch dieser Beitrag entsteht KI-nativ; die Messungen macht das aber nicht weniger echt — die Methode steht hier.


Wenn dich solche Messreihen interessieren: Newsletter.