mothership/headless-shopware-varnish-cache
A Laravel package for integrating Varnish cache with headless Shopware storefronts. It helps manage cache tags, purging, and invalidation so product, category, and content updates stay fresh while keeping cached responses fast.
Install via Composer and publish the package assets:
composer require mothership/headless-shopware-varnish-cache
php artisan vendor:publish --tag=headless-shopware-varnish-cache
Version check first. As of 3.0.0 the package targets Shopware 6.6. If your Shopware instance is still on 6.5, stay on the 2.0.1 line — the 3.x major bump tracks the 6.6 API/admin changes, not a drop-in. Pin the constraint in composer.json deliberately.
Then wire up the required environment values (the config file is the source of truth — read it first; it drives almost everything else):
SHOPWARE_ADMIN_URL, client ID, secret)Where to look first:
config/headless-shopware-varnish-cache.php — understand the purge transport, tag strategy, and which Shopware entities are wired up.First meaningful use case: invalidate a single product page's cache when a product is edited in Shopware. Typically this is a webhook from Shopware Admin to a route the package exposes, or a manual call to the purge service from an Eloquent observer / job. Test it with a curl -X PURGE against one Varnish instance before wiring up the full flow.
This package sits in the middle of a three-party contract: Shopware (source of truth), your Laravel headless layer (renders the storefront API), and Varnish (caches responses). Day-to-day patterns:
Webhook-driven invalidation. Register Shopware Admin webhooks for product.written, category.written, sales_channel.written etc., point them at this package's route, and let it translate Shopware entity events into Varnish purges. This is the single biggest win — you stop guessing at cache TTLs and purge precisely.
Surrogate-key / tag-based purging. Rather than purging URLs (fragile when slugs change), emit tags on the response — product ID, category ID, sales channel ID — and purge by tag when the entity changes. This scales when one product appears on 20 category pages and 40 CMS landing pages.
Middleware on storefront responses. Usually the package ships middleware that adds the tag headers to outgoing responses. Put it on every route that proxies or renders product/category/CMS data, otherwise you're caching responses with no tag handle to invalidate.
Queue the purge calls. Varnish purges are HTTP round-trips to N hosts. Wrap the service call in a queued job so Product saves in bulk don't stall the admin panel.
Eloquent observer bridge (optional). If you mirror Shopware data into local tables for your storefront, hook observers to the package's purge by tag so updating a local copy also flushes Varnish. Keep it out of the request cycle.
Local testing — start from the example Docker setup. 3.0.0 ships a working example Docker environment (Varnish in front of the app, with the ACL/purge config already wired). Use it as your baseline rather than hand-rolling a default.vcl — copy it, point it at php artisan serve or Sail, and confirm both the response headers and the purge behavior before touching staging. It's also the best documentation of what the package actually expects from Varnish.
Version/ecosystem pairing is now the first thing to check. 3.0.0 = Shopware 6.6. 2.0.1 = 6.5. Upgrading the package without upgrading Shopware (or vice versa) is the fastest way to get confusing 404s on admin endpoints or unrecognised webhook payloads. Confirm your Shopware version before bumping.
The Docker example is documentation, not production config. Treat the shipped default.vcl and compose file as a reference implementation: it's tuned for a single local Varnish, likely without your real ACLs, TLS terminator, or multi-host purge fan-out. Copy the shape, not the settings.
Tag format mismatch is the most common failure. Shopware, Varnish, and this package must all agree on the tag key format (e.g. product-1234 vs P1234). If a purge silently does nothing, compare the header emitted by your Laravel response with the tag the package sends on purge — a one-character mismatch is invisible. The 6.6 migration touched entity/sales-channel handling, so re-verify tags after upgrading.
Purge auth is on Varnish, not Laravel. Varnish needs an ACL/secret permitting PURGE and BAN from your Laravel host. Without it, the package returns 200-ish errors or silent rejects. Verify with a raw curl -X PURGE before blaming the package — the example Docker setup shows what a working ACL looks like.
URL normalization bites. Varnish hashes the exact request URL — protocol, host, trailing slash, query string order. If your purge URL differs from the cache key URL by even a query param, you purge nothing. Prefer tags over URLs whenever possible.
Header size limits. Surrogate-Key / X-Cache-Tags headers have practical limits (typically ~8 KB across most hops). A category page with hundreds of product tags will blow this — split into a hierarchy of tags (category-12, product-99) rather than enumerating every SKU on the page.
Still a niche package. Small maintainer surface, zero known dependents, and 3.0.0 is the first release in a long while (previous was 2.0.1, May 2024). The 6.6 support is a good sign of life, but still: read the source (it's small — a few hundred lines), confirm Laravel 11/12 compatibility yourself, and be prepared to fork rather than wait for upstream fixes. Fine for a well-scoped cache purger, not for anything security-sensitive.
Headless means no Storefront safety net. If Shopware's own Storefront is bypassed, nothing validates that your cache tags actually correspond to entities you render. Write an integration test that edits a product via Admin API, fires the webhook, and asserts the page returns fresh data.
Debugging checklist. 1) curl -I the page and confirm the tag header. 2) curl -X PURGE <url> and watch Varnish's varnishlog -g request -q "ReqMethod eq 'PURGE'". 3) Check your queue for failed purge jobs. 4) Enable package debug logging if provided; if not, add a Log::debug around the service call. 5) Reproduce inside the example Docker setup to rule out your own Varnish config.
Extension point. The tag-generation and purge-transport are the two places worth wrapping in your own decorators. If the package hardcodes tag format, subclass or bind your own implementation in the container rather than patching vendor code — you'll need to upgrade (or fork) eventually.
How can I help you explore Laravel packages today?