Headless-Shops sind 2026 schnell - solange die Store API mitspielt. Genau hier setzt Shopware ab Version 6.7 an: Ausgewählte, nicht-mutierende Store-API-Endpoints sind nun cachebar und liefern echte Cache-Control-Header aus. Damit verschiebt sich ein großer Teil der Last vom Origin auf Reverse Proxy, CDN und das Frontend. Der Hebel ist erheblich: Bereits eine 0,1 Sekunde schnellere Antwort steigerte die Conversion im Handel um 8,4 % und den durchschnittlichen Bestellwert um 9,2 % (Deloitte) - und gute Core Web Vitals heben die Conversion um 15 bis 30 % (web.dev). Dieser Leitfaden zeigt, wie Sie in Nuxt- und Composable-Frontends N+1-Probleme vermeiden, Client-Caching sauber setzen und Stale-While-Revalidate produktiv nutzen - rein auf der Caching-Schicht der Store API, abgegrenzt von der reinen Architektur-Frage.
Warum Store-API-Caching der Performance-Hebel 2026 ist
In einem Headless-Setup rendert das Frontend - etwa Nuxt oder ein Composable-UI - die Oberfläche und holt sich Daten über die Store API. Anders als die klassische Storefront, die ihren HTTP-Cache und ESI mitbringt, liefen Store-API-Antworten lange ungecacht direkt aus dem Origin. Jeder Seitenaufruf, jede Kategorie, jeder Produktblock erzeugte einen frischen PHP-Request samt Datenbankzugriff. Bei wachsendem Traffic wird das zum Flaschenhals.
Die Zahlen machen den Druck deutlich: 53 % der mobilen Nutzer verlassen eine Seite, die länger als 3 Sekunden lädt (Google/SOASTA), und pro zusätzlicher Sekunde Ladezeit sinkt die Conversion um rund 7 % (Conductor). Bereits 100 ms Mehrzeit können rund 1 % Umsatz kosten (Amazon via Conductor). Wer die Store API cachebar macht, adressiert genau diese Latenz - nicht im Frontend-Code, sondern an der teuersten Stelle: der Server-Antwortzeit.
Dieser Artikel behandelt ausschließlich die Caching-Schicht der Store API. Wenn Sie zunächst die grundsätzliche Entscheidung für ein entkoppeltes Frontend treffen wollen, lesen Sie Headless Commerce mit Shopware und Composable Commerce. Hier geht es um die konkrete Caching-Mechanik, nicht um die Architektur-Frage.
Was sich ab Shopware 6.7 konkret ändert
Mit dem überarbeiteten Caching-System (eingeführt über das CACHE_REWORK-Feature-Flag und seit den 6.7.5/6.7.6-Releases produktiv ausgerollt) markiert Shopware ausgewählte Store-API-Routen als cachebar. Cachebar ist eine Route, wenn sie das Routen-Attribut '_httpCache' => true trägt - also nicht-mutierende Endpoints, die keine sensiblen, kundenindividuellen Daten zurückgeben (Shopware Docs).
Cachebare Routen liefern standardmäßig folgenden Cache-Control-Header (Shopware Docs):
Cache-Control: public, max-age=0, s-maxage=1800,
stale-while-revalidate=86400, stale-if-error=7200
# Nicht-cachebare Route:
Cache-Control: no-cache, privates-maxage=1800
Shared Caches (Reverse Proxy, CDN) halten die Antwort 30 Minuten frisch. max-age=0 zwingt den Browser dagegen zur Revalidierung - so steuern Sie Server- und Client-Cache getrennt.
stale-while-revalidate=86400
Bis zu 24 Stunden darf der Cache eine veraltete Antwort sofort ausliefern und parallel im Hintergrund erneuern. Der Nutzer wartet in der Regel nicht auf den Origin.
stale-if-error=7200
Fällt der Origin aus, serviert der Cache für 2 Stunden weiter die letzte gute Antwort - ein eingebautes Sicherheitsnetz für Spitzenlast.
public
Die Antwort darf von gemeinsam genutzten Caches gespeichert werden - die Voraussetzung dafür, dass ein Reverse Proxy oder CDN überhaupt eingreift.
Damit getrennte Sprachen, Währungen und Kontexte nicht denselben Cache-Eintrag teilen, nutzt Shopware statt Cookies drei dedizierte Header, die im Vary-Header geführt werden: sw-currency-id, sw-language-id und sw-context-hash (Shopware Docs). So bekommt ein DE/EUR-Besucher so nicht versehentlich die EN/USD-Antwort aus dem Cache.
GET statt POST: Voraussetzung fürs Caching
HTTP-Caches speichern in der Regel nur GET-Antworten. Viele Store-API-Aufrufe nutzten bisher POST, um komplexe Filter- und Such-Kriterien im Body zu übergeben. Damit diese Requests cachebar werden, können Kriterien jetzt als komprimierter, kodierter String im GET-Parameter _criteria mitgegeben werden - intern als JSON -> gzip -> base64url kodiert (Shopware Docs).
// Cachebarer GET-Request mit kodierten Kriterien
const criteria = {
limit: 24,
associations: { cover: {}, manufacturer: {} },
filter: [{ type: 'equals', field: 'active', value: true }]
}
const encoded = encodeCriteria(criteria) // JSON -> gzip -> base64url
const res = await $fetch(
`/store-api/product?_criteria=${encoded}`,
{ headers: { 'sw-access-key': key } }
)
// Antwort traegt jetzt einen public Cache-Control-HeaderDie SDK-Helfer kanonisieren die Kriterien vor dem Kodieren - gleiche Anfrage, gleicher String, gleicher Cache-Key. Wer Kriterien selbst zusammenbaut, sollte Reihenfolge und Felder stabil halten, sonst sinkt die Hit-Rate, weil semantisch identische Requests unterschiedliche Cache-Einträge erzeugen.
N+1-Probleme in Nuxt und Composable-Frontends vermeiden
Das häufigste Performance-Leck im Headless-Frontend ist nicht der fehlende Cache, sondern das N+1-Muster: Eine Listenkomponente rendert 40 Produkte und feuert pro Produkt einen eigenen Detail-Call. Aus einem Request werden 41 - und selbst mit Caching bleibt das ineffizient, weil jeder Call eigene Latenz, eigene Verbindung und eigene Revalidierung mitbringt.
| Muster | Calls pro Listenseite | Latenz-Risiko | Cache-Effizienz |
|---|---|---|---|
| N+1 (pro Produkt 1 Call) | 41 | Hoch | Gering |
| Gebatcht (associations) | 1-2 | Niedrig | Hoch |
| Gebatcht + SWR | 1-2 (oft aus Cache) | Sehr niedrig | Sehr hoch |
Die Lösung liegt in der Store API selbst: Statt Nachladen pro Produkt fordern Sie verbundene Daten über associations und includes in einem einzigen Aufruf an. Das reduziert nicht nur die Anzahl der Requests, sondern auch die übertragene Datenmenge - includes schneidet die Antwort auf die wirklich benötigten Felder zu.
// Statt 40 Einzel-Calls: ein gebatchter, schlanker Request
const { data } = await useAsyncData('listing', () =>
storeApi('/store-api/product-listing/' + categoryId, {
method: 'GET',
query: { _criteria: encodeCriteria({
associations: { cover: {}, options: {} },
includes: {
product: ['id', 'name', 'calculatedPrice', 'cover'],
product_media: ['url']
}
}) }
})
)
// useAsyncData dedupliziert parallele Aufrufe automatisch- Batching statt Nachladen: Verbundene Entitäten über
associationsin einem Call holen, nicht pro Produkt einzeln. - Payload schlank halten: Mit
includesnur die Felder anfordern, die das UI tatsächlich rendert - weniger Bytes, schnelleres Parsen. - Request-Deduplizierung:
useAsyncData/useFetchin Nuxt bündeln identische parallele Aufrufe zu einem Request mit gemeinsamem Key. - Pagination vor Prefetch: Erst sichtbare Seite laden, Folgeseiten erst bei Bedarf oder im Idle prefetchen.
Client-Caching im Frontend richtig setzen
Server-Caching adressiert die Antwortzeit, Client-Caching die Navigation. Ein Composable-Frontend kann bereits geladene Store-API-Antworten im Speicher halten und bei erneutem Aufruf sofort aus dem normalisierten Cache rendern - während im Hintergrund revalidiert wird. So fühlt sich der Seitenwechsel instant an. Studien beziffern den Effekt: React Server Components und schlanke Hydration reduzieren das JavaScript-Bundle um 40 bis 60 % (digitalapplied) und verbessern damit direkt INP und LCP.
Wichtig ist die Abstimmung mit den Server-Headern. Da max-age=0 gesetzt ist, revalidiert der Browser - aber ein bedingter Request mit ETag oder If-None-Match endet bei unveränderten Daten in einem schlanken 304 Not Modified statt einer vollen Antwort. Das spart Bandbreite, ohne Frische zu opfern.
Memory-Cache
Bereits geladene Listen und Produkte im State-Store halten. Wiederkehrende Navigation rendert ohne neuen Netzwerk-Roundtrip.
ETag / 304
Bedingte Requests nutzen: unveränderte Daten liefern 304 Not Modified - der Body wird gar nicht erst übertragen.
Prefetch im Idle
Wahrscheinliche nächste Routen (z. B. Top-Kategorie) im Leerlauf vorab laden, damit der Klick instant wirkt.
Warenkorb, Kundenkonto und Preise mit kundenspezifischen Rabatten gehören NICHT in einen public-Cache. Diese Endpoints bleiben no-cache, private. Trennen Sie cachebare Katalogdaten strikt von kontextabhängigen Daten - sonst riskieren Sie, dass ein Nutzer fremde oder veraltete personalisierte Inhalte sieht.
Stale-While-Revalidate produktiv nutzen
Stale-While-Revalidate (SWR) ist der wirksamste Hebel für wahrgenommene Geschwindigkeit. Das Prinzip: Läuft s-maxage ab, liefert der Cache die veraltete Antwort trotzdem sofort aus und stößt parallel im Hintergrund eine Aktualisierung an. Niemand wartet auf den Origin - der nächste Besucher bekommt bereits die frische Version. In der Praxis senken SWR-Header die Origin-Zugriffe um rund 65 % (digitalapplied).
Stale-While-Revalidate entkoppelt die wahrgenommene Geschwindigkeit von der tatsächlichen Origin-Antwortzeit: Der Nutzer sieht praktisch durchgehend sofort Inhalt, die Aktualisierung passiert unsichtbar im Hintergrund.
XICTRON Entwicklungsteam
Die Shopware-Defaults sind bewusst großzügig gewählt: stale-while-revalidate=86400 (24 Stunden) und stale-if-error=7200 (2 Stunden) sorgen dafür, dass selbst bei seltenem Traffic oder kurzem Origin-Ausfall noch Inhalte ausgeliefert werden. Für schnelllebige Inhalte - etwa Verfügbarkeiten oder Aktionspreise - lassen sich pro Route engere Werte über benannte Caching-Policies konfigurieren, die Cache-Control je Bereich (storefront, store_api) und Route bilden.
# Eigener Backend-for-Frontend-Layer, der mehrere
# Store-API-Quellen bündelt, kann engere Werte setzen:
Cache-Control: public, s-maxage=60, stale-while-revalidate=600Setzen Sie s-maxage so kurz wie nötig für Frische und stale-while-revalidate so lang wie möglich für Verfügbarkeit. So bleibt die Hit-Rate hoch, ohne dass Nutzer veraltete Inhalte über längere Zeit sehen - der Hintergrund-Refresh hält den Cache aktuell.
Cache-Invalidierung: frisch, ohne Flaschenhals
Ein Cache ist nur so gut wie seine Invalidierung. Aendert sich ein Preis oder Lagerbestand, muss der betroffene Eintrag gezielt verfallen - nicht der gesamte Cache. Shopware arbeitet hier mit Cache-Tags, die das Object-Cache, den HTTP-Cache und - sauber konfiguriert - auch den Edge-Layer synchronisieren. So bleibt die Hit-Rate hoch, ohne dass veraltete Daten hängenbleiben.
- Tag-basiert invalidieren: Bei Produktänderung nur die zugehörigen Cache-Tags löschen, nicht pauschal flushen.
- SWR als Puffer: Selbst direkt nach Ablauf liefert der Cache sofort aus und holt frische Daten im Hintergrund - keine Lastspitze auf dem Origin.
- Monitoring der Hit-Rate: Eine sinkende Hit-Rate ist oft das erste Signal für fragmentierte Cache-Keys oder zu enge TTLs - regelmäßig die Performance überwachen.
- Kontext-Header prüfen: Falsch gesetzte
Vary-Header fragmentieren den Cache und senken die Hit-Rate drastisch.
Store-API-Caching und Edge-Caching für Shopware ergänzen sich: Die Store API liefert die Cache-Control-Header, der Edge-Node setzt sie global um. Wer beides kombiniert, bringt API-Antworten weltweit unter die 50-ms-Marke.
Messen, was zählt: Headless-Performance-Audit
Caching wirkt nur, wenn es gemessen wird. Ein Headless-Performance-Audit prüft, welche Store-API-Routen tatsächlich cachebar sind, wie hoch die reale Hit-Rate liegt, ob N+1-Muster im Frontend lauern und ob die Vary-Header sauber gesetzt sind. Die Wirkung ist messbar: Edge- und API-Caching reduzieren Origin-Requests typischerweise um 85 bis 95 % (digitalapplied) und die TTFB um 60 bis 80 % (Cloudflare).
Gleichzeitig gilt: Ein schlecht geplanter Headless-Umbau kann Organic Traffic um 20 bis 40 % kosten (digitalapplied), wenn Rendering, Caching und SEO nicht zusammenpassen. Genau deshalb gehören Caching-Schicht und Core-Web-Vitals-Optimierung in dieselbe Analyse. XICTRON prüft beide Ebenen gemeinsam und leitet konkrete, priorisierte Maßnahmen ab.
- Cachebare Store-API-Routen identifiziert und
_httpCachekorrekt gesetzt - GET mit
_criteriastatt POST für cachebare Abfragen - N+1-Muster im Nuxt/Composable-Frontend durch Batching ersetzt
Vary-Header (sw-currency-id,sw-language-id,sw-context-hash) korrekt- Personalisierte Endpoints strikt von
public-Cache getrennt - Tag-basierte Invalidierung und Hit-Rate-Monitoring etabliert
Backend-for-Frontend: mehrere Quellen sauber bündeln
Viele Headless-Seiten kombinieren mehrere Store-API-Antworten zu einer Ansicht: eine Produktliste, dazu das Navigationsmenü, Cross-Selling und ein CMS-Block. Werden diese Aufrufe direkt aus dem Browser gefeuert, summieren sich Latenzen und Verbindungen. Ein Backend-for-Frontend (BFF) bündelt sie serverseitig zu einer einzigen, vorbereiteten Antwort - und kann dabei eigene, engere Cache-Control-Werte setzen, weil es die Frische der Mischung selbst kennt.
Der Effekt ist doppelt: Erstens entfällt für den Client der Wasserfall aus Einzelrequests, zweitens wird die aggregierte Antwort selbst cachebar. Ein BFF mit public, s-maxage=60, stale-while-revalidate=600 liefert die Komposition 60 Sekunden frisch und danach bis zu zehn Minuten als Stale-Antwort, während im Hintergrund neu zusammengesetzt wird. Wichtig ist, dass das BFF die Kontext-Header (sw-language-id, sw-currency-id, sw-context-hash) durchreicht und im Vary führt, damit die Aggregation nicht über Sprach- und Währungsgrenzen hinweg verwechselt wird.
Ein BFF sollte komponieren, nicht personalisieren. Sobald kundenspezifische Daten wie Warenkorb oder individuelle Preise einfließen, fällt die Antwort aus dem public-Cache. Trennen Sie den cachebaren Katalog-Teil vom personalisierten Teil und laden Sie Letzteren separat im Client nach - so bleibt der große, teure Teil der Seite cachebar.
In der Praxis lohnt sich das BFF besonders für Einstiegs- und Kategorieseiten mit viel statischem Katalog-Anteil. Produktdetailseiten profitieren ebenfalls, solange Verfügbarkeit und Preis nicht sekundenaktuell sein müssen. Wo doch, kombiniert man einen cachebaren Grundzustand mit einem kleinen, nicht gecachten Live-Request für den volatilen Teil.
Typische Caching-Fehler und wie Sie sie vermeiden
Caching wirkt nur, wenn es konsequent gedacht ist. In Headless-Projekten begegnen uns wiederholt dieselben Muster, die die Hit-Rate drücken oder - schlimmer - falsche Daten ausliefern. Die gute Nachricht: Sie lassen sich mit klaren Regeln vermeiden.
Unstrukturierte Cache-Keys
Wenn Kriterien in wechselnder Reihenfolge oder mit überflüssigen Feldern kodiert werden, entstehen viele leicht unterschiedliche Keys für dieselbe Anfrage. Lösung: Kriterien vor dem Kodieren kanonisieren und nur tatsächlich genutzte Felder anfragen.
Personalisierung im public-Cache
Wer kundenspezifische Preise oder Empfehlungen versehentlich in eine cachebare Route mischt, riskiert, dass ein Besucher fremde Daten sieht. Lösung: personalisierte Bestandteile strikt in private-Routen oder Client-Calls auslagern.
Zu aggressive Invalidierung
Ein pauschaler Cache-Flush bei jeder Änderung setzt die Hit-Rate auf null und erzeugt Lastspitzen. Lösung: tag-basiert nur betroffene Einträge invalidieren und Stale-While-Revalidate als Puffer nutzen.
Fehlende Vary-Header
Ohne korrekte Vary-Angaben teilen sich Sprachen und Währungen denselben Eintrag - oder der Cache fragmentiert unnötig. Lösung: genau die drei Kontext-Header führen, die der Antwort zugrunde liegen.
Hinzu kommt ein organisatorischer Punkt: Caching ist kein einmaliges Setup, sondern ein laufender Prozess. Sortimente, Preise und Aktionen ändern sich, das Frontend wird weiterentwickelt, neue Routen kommen hinzu. Deshalb gehört die Hit-Rate ins Monitoring und die Caching-Strategie in jede größere Release-Planung. So bleibt der Performance-Gewinn dauerhaft erhalten, statt nach wenigen Wochen wieder zu verpuffen.
Schrittweise einführen: Caching ohne Risiko aktivieren
Store-API-Caching lässt sich gefahrlos und schrittweise aktivieren, weil das überarbeitete Caching-System hinter einem Feature-Flag liegt und die Defaults konservativ gewählt sind. In der Praxis empfiehlt sich ein gestufter Rollout: erst auf einer Staging-Umgebung messen, dann auf gut cachebaren Katalogrouten beginnen und die Hit-Rate beobachten, bevor weitere Routen folgen. So sehen Sie den Effekt, bevor er produktiv wirkt, und können die TTL-Werte an Ihr Sortiment anpassen.
Besonders wichtig ist die Reihenfolge: Zuerst die N+1-Muster im Frontend beseitigen, dann das Server-Caching aktivieren. Wer zuerst cacht, ohne das Frontend zu entzerren, cacht lediglich viele kleine, ineffiziente Antworten - der Gewinn bleibt hinter dem Möglichen zurück. Erst die Kombination aus gebatchten, schlanken Requests und cachebaren Antworten entfaltet die volle Wirkung. Diese Reihenfolge spiegelt sich auch in einem Headless-Performance-Audit wider, das vom Frontend-Code bis zur Cache-Konfiguration durchgeht.
Nicht zuletzt zahlt sich Caching auf die Betriebskosten aus: Weniger Origin-Requests bedeuten weniger CPU-Last, kleinere Server und ruhigere Lastspitzen zu Aktionszeiten. Was als reine Performance-Maßnahme beginnt, entlastet damit auch das Hosting - ein Effekt, der mit wachsendem Traffic deutlicher wird und die Investition in eine saubere Caching-Schicht über die reine Geschwindigkeit hinaus rechtfertigt.
So könnte Ihr Headless-Shop aussehen:
Elektronik-Shop
Dieser Artikel basiert auf Daten und Dokumentation aus: Shopware Documentation (Store API, Caching Strategy ADR, Release Notes 6.7), Deloitte (Milliseconds Make Millions), web.dev, Google/SOASTA, Conductor (Amazon-Studie), digitalapplied (Headless Commerce 2026) und Cloudflare. Die genannten Zahlen können je nach Zeitpunkt, Shop und Messmethode variieren.
Cachebar sind in der Regel nicht-mutierende GET-Endpoints, die keine kundenindividuellen Daten zurückgeben - etwa Produkt-, Listing-, Kategorie- und Navigationsabfragen. Eine Route ist cachebar, wenn sie das Attribut _httpCache trägt. Warenkorb, Konto und personalisierte Preise bleiben typischerweise no-cache, private (Shopware Docs).
Der Standard für cachebare Routen lautet public, max-age=0, s-maxage=1800, stale-while-revalidate=86400, stale-if-error=7200. s-maxage steuert Shared Caches (30 Min frisch), max-age=0 zwingt den Browser zur Revalidierung, SWR liefert bis zu 24 Stunden veraltete Inhalte sofort aus und erneuert im Hintergrund. Die Werte lassen sich pro Route anpassen.
Statt pro Produkt einen eigenen Call zu feuern, holen Sie verbundene Daten über associations und schneiden die Antwort mit includes zu - idealerweise in einem einzigen gebatchten Request. In Nuxt dedupliziert useAsyncData/useFetch parallele Aufrufe zusätzlich. So sinkt die Zahl der Calls erfahrungsgemäß deutlich.
HTTP-Caches speichern in der Regel nur GET-Antworten. Damit Abfragen mit komplexen Kriterien cachebar werden, können die Kriterien als komprimierter String im GET-Parameter _criteria übergeben werden (JSON, gzip, base64url). POST-Requests werden üblicherweise nicht gecacht.
Kurzzeitig ja - genau das ist gewollt. SWR liefert nach Ablauf der Frische sofort die letzte Antwort und erneuert sie im Hintergrund, sodass der nächste Besucher bereits die aktuelle Version sieht. Für schnelllebige Inhalte sollten Sie s-maxage und SWR-Fenster bewusst kürzer setzen oder die Route nicht cachen.
Ja. Bereits ein Reverse Proxy vor dem Shopware-Origin profitiert von den Cache-Control-Headern und entlastet die PHP- und Datenbankschicht spürbar. Ein global verteiltes Edge-Netz verstärkt den Effekt zusätzlich, ist aber keine Voraussetzung. Die genaue Wirkung hängt von Traffic, Cache-Strategie und Shop-Struktur ab.