arrow_left_alt

Blog

Shopware 6.6 vs 6.7: Key differences and a deep dive into caching and ESI

September 25, 2025

As Shopware continues to evolve, the release of version 6.7 marks a major step forward in terms of architecture, performance, and developer experience. In today’s blog, we’ll explore the major changes introduced in Shopware 6.7 compared to 6.6, and then take a deeper look into one of the most impactful updates for developers and merchants alike: the revamped caching strategy and the introduction of Edge Side Includes (ESI).

High-Level Changes in Shopware 6.7

Here’s a quick overview of what changed between 6.6 and 6.7:

New caching strategy & ESI in Shopware 6.7

The most significant backend performance improvement in Shopware 6.7 is the introduction of delayed cache invalidation and Edge Side Includes (ESI). These changes fundamentally alter how dynamic content is served and cached.

Delayed cache invalidation

Prior to 6.7, Shopware used immediate cache invalidation. Every entity update (like a product, category, or CMS page) would instantly invalidate relevant cache entries, potentially reducing cache hit ratios and increasing server load during peak traffic.

What changed:

  • Cache invalidation is delayed (default: every 5 minutes).
  • The new scheduled task <kbd>shopware.invalidate_cache</kbd> handles batch invalidations.
  • Improves stability and reduces unnecessary cache churn.

Benefits:

  • Increased cache hit ratio
  • Improved page load performance
  • Lower CPU usage on frequent writes

Considerations for Developers:

  • Test your plugins with delayed invalidation logic
  • Time-sensitive content should be flagged as non-cacheable or handled via ESI

Removal of Store-API object cache

The legacy Store-API route caching layer is gone. All <kbd>Cached*Route</kbd> classes (which used an internal cache for Store API calls) were removed. Instead, the Store API responses are now served entirely via the HTTP cache. This drastically reduces the amount of cache entries. The release notes emphasize that this causes minimal performance impact, since the old store-API cache had low hit rates anyway, but it cuts the total number of cache items and invalidations.

<divclass="rtb-text-box is-blue-50">Developer note: any plugin that extended or referenced <kbd>AbstractCached*Route</kbd> or similar should be updated; you now rely on the HTTP cache and possibly use AJAX or ESI for any dynamic parts (see below) instead of custom route caching.</div>

Edge Side Includes (ESI)

ESI is a powerful mechanism that lets the reverse proxy (like Varnish or HTTP cache) serve a mostly cached page while fetching specific dynamic fragments separately. Shopware 6.7 implements ESI rendering in the Storefront to improve performance for pages that include dynamic components.

How It Works

  • Full pages can be cached, with dynamic blocks (e.g., cart sidebar, login status) rendered separately.
  • These fragments are fetched via individual HTTP requests and composed at the edge.
  • Improves Time To First Byte (TTFB) and reduces load on core application logic.

Shopware’s implementation

A key frontend change in 6.7 is that the storefront header and footer are rendered via ESI. Two new routes were added <kbd>(/_esi/global/header and /_esi/global/footer)</kbd>, typically handled by a <kbd>NavigationController</kbd>. The base Twig template now includes the header and footer with the Twig <kbd>render_esi()</kbd> function, which inserts special ESI tags. 

In other words, the header and footer HTML are generated by separate requests  <kbd>(frontend.header and frontend.footer)</kbd> and merged by the HTTP cache (Symfony or Varnish) when serving pages. Similarly to an AJAX call but on the server side before serving the full response to the client. As the release notes describe: “The header and footer are now loaded via ESI. This allows caching the header and footer separately from the rest of the page”. Two new entry-point templates were added  <kbd>(views/storefront/layout/header.html.twig and .../footer.html.twig)</kbd>, which extensions should extend (the internal block names remain the same).

Because header/footer are now independent cache fragments, changes to navigation (like adding a category) only invalidate the header cache, not every page in the store.

Numbers

In theory, it sounds great, the ability to cache separate parts of HTML independently creates lots of possibilities. Let’s see it in action

Product page TTFB on clean Shopware instances with demo data and Symfony HTTP Cache:

  • First visit ~90ms
  • Cached ~10ms
  • Product cache invalidated ~60ms
  • Header invalidated ~47ms
  • Footer invalidated ~30ms

This example showcases how ESI resolves the problem of invalidating the whole content of a page when just a part of it changes.

{{cta-technology-shopware="/comp/cta"}} 

Practical Use Cases

Now it’s easier to use HTTP cache while on <kbd>'logged-in', 'cart-filled’ </kbd> sessions. Instead of writing custom AJAX calls for personalized content, you can leverage ESI and omit creating loaders on the frontend.

Developer Notes

  • Cache invalidation timing: Plugins that rely on immediate cache invalidation (for example, expecting data to disappear instantly after save) will see that changes now take effect only after the next run of the scheduled task. During development you may need to manually run the <kbd>InvalidateHttpCacheTask</kbd> or click the “Update cache” button in the Admin. (There is currently no new event triggered right after delayed invalidation.) Conversely, you can force immediate invalidation by adding the header <kbd>sw-force-cache-invalidate: 1</kdb> to an API write request.
  • Store-API cache code: If your plugin extended or subscribed to store-API cache events (e.g. using <kbd>@CacheResult</kbd> or extending <kbd>AbstractCached*Route</kbd>), remove those and rely on the HTTP cache. Store-API controller hooks still fire, but caching is now done at the HTTP layer. The simplified cache key means your plugins should not add complex context rules via <kbd>Context::addState</kbd>; instead handle customer-specific content via AJAX or separate ESI fragments.
  • Header/footer customization: If you overrode the base layout or header/footer templates, you must now extend the new Twig entry points <kbd>(layout/header.html.twig and layout/footer.html.twig)</kbd> instead of injecting into <kbd>page.header</kbd>. The block names are unchanged, but the overall structure moved into these includes. For example, hooks into <kbd>pagelet.navigation</kbd> now apply only to the <kbd>/_esi/global/header</kbd> route.
  • Varnish/CDN setup: If using Varnish or a CDN, update your configuration to require XKey support. The <kbd>Xkey</kbd> header (set by Symfony’s HttpCache) must be honored for cache purges. The old ban-via-Redis method is no longer supported (and indeed Shopware’s own VCL now uses <kbd>xkey.purge</kbd> or <kbd>xkey.softpurge</kbd>. Ensure your reverse-proxy config (and <kbd>shopware.http_cache.reverse_proxy</kbd> settings) match the new defaults.
  • Event subscribers: Most cache-related events (e.g. product save, cart change) are still dispatched, but note that HTTP invalidation subscribers may see delayed effects. If you wrote custom event handlers for cache tags, verify they work in the new model. Also, the <kbd>CacheInvalidateEvent</kbd> (for object cache) is largely irrelevant for storefront pages now.
  • Removed classes: Any code importing now-removed classes (like <kbd>CacheInvalidation, ConfigurationNotFoundException, CachedProductRoute</kbd>, etc.) will need to be updated, as noted in the 6.7 upgrade docs.

Conclusion

Shopware 6.7 brings several architectural improvements, but the most impactful for storefront performance is the shift to delayed cache invalidation combined with ESI fragment rendering. These tools provide developers with greater flexibility and control while boosting performance at scale.

If you're developing high-traffic eCommerce platforms or complex plugins, understanding and leveraging these caching strategies will be critical going forward.

<div class="ml-form-embed"
 data-account="2253895:i2a9g5f0y9"
 data-form="5904351:c1f9r4">
</div>