Shopware 6 performance tuning, in the order that actually pays
The Shopware-specific work that moves a slow shop: HTTP cache and cache tags, the N+1 hiding in your criteria, the admin worker, when Elasticsearch is worth it — and the tuning that is pure cargo cult.
Photo: free stock photography (Unsplash licence) — see imprint
Almost every slow Shopware 6 shop is slow for one of four reasons
We get called into slow shops often enough that the diagnosis has become boring. It is nearly always the HTTP cache being bypassed or invalidated far too aggressively, a plugin doing database work per product inside a loop, the message queue running in the admin instead of on the CLI, or product images being generated at request time because nobody ran the thumbnail job. Four causes. In that rough order of frequency.
What it is almost never: the server being too small, the PHP version, or any of the config values people paste into forums. We have watched a shop move to hardware costing three times as much and get 8% faster, because the bottleneck was a plugin issuing 240 queries on the category page. Hardware multiplies your architecture. If the architecture is doing 240 queries, you are buying a faster way to do 240 queries.
So the order below is deliberate. It is sorted by how much time it buys per hour spent, not by how interesting it is to work on. The interesting work is at the bottom.
First: find out whether the HTTP cache is actually hitting
Shopware 6 ships a reverse-proxy-style HTTP cache, and when it works a category page is served without PHP touching the database at all. When it does not work, every single visitor pays the full render. The first thing to check is not a setting — it is whether your pages are being cached at all. A session created on the first request, a plugin that reads the cart on every page, a cookie set by a tracking snippet: any of these can quietly make each page uncacheable, and nothing in the interface will tell you.
The second half of the same problem is invalidation. Cache tags let Shopware throw away only the pages affected by a change — edit one product, drop the pages that show it. Plugins that invalidate too broadly turn every price update into a full cache flush, and on a large catalogue that means the shop spends its afternoon rebuilding pages nobody asked for. A shop that flushes everything hourly does not have a cache; it has a warm-up loop. Checking hit rate and invalidation scope is an afternoon and it is the highest-value afternoon in this list.
Second: the N+1 hiding in your criteria
Shopware's data abstraction layer is pleasant to write against and very easy to abuse. You fetch a list of products, then loop over them, and inside the loop you ask for the manufacturer, or the cheapest price, or a custom field. Each of those asks is a query. Thirty products on a listing page, three lookups each, and you have ninety round trips to the database where one association in the criteria would have done. The code reads perfectly. It is the single most common performance bug we find in custom plugins.
The fix is to declare what you need up front — add the associations to the criteria, fetch once, use the result. The finding is the hard part, not the fixing. Turn on the profiler on a staging copy, load your three worst pages, and read the query count. If a listing page issues more queries than it shows products, you have found your afternoon's work, and it is usually worth more than every other item on this page combined.
Third: the boring infrastructure that nobody set up
The admin worker is the classic. Out of the box, Shopware can process its message queue through the administration interface — convenient for a local install, wrong for production. It means your queue only moves while somebody has the admin open, and it means background work competes with the browser tab. Move the consumer to a proper CLI process under a supervisor and the shop stops behaving strangely at 6pm when the last person closes their laptop.
Thumbnails are the other one. If your media settings generate image sizes on demand, the first visitor to every product page pays for image processing in their page load. Generate them ahead of time, serve them from disk or a CDN, and make sure your theme is actually requesting the generated sizes rather than the 4000-pixel original the supplier sent. We have seen product pages shipping eleven megabytes of images because nobody looked.
- Message consumer on the CLI under a supervisor — never the admin worker in production.
- Thumbnails generated ahead of time, and a theme that requests them.
- Cache invalidation scoped by tag, not a full flush on every price change.
- Cron running reliably, so indexing does not pile up until it stampedes.
Elasticsearch: worth it later than people think
Elasticsearch is the fashionable answer and it is genuinely excellent — above a threshold. Below that threshold you have added a second data store, a sync process, an indexing job and a whole new failure mode to a shop whose MySQL search was fine. Our rough rule: a catalogue in the low tens of thousands of products, with filter-heavy listings and real search traffic, benefits clearly. A few thousand products with a handful of filters does not, and the operational cost is permanent while the speed gain is invisible.
The honest test is to measure your product listing and search response times first. If they are already respectable and your slow page is the checkout, Elasticsearch will change nothing except your monthly bill and your on-call rota. Fix the thing that is slow. That sounds too obvious to write down, and it is the advice most often ignored in this particular decision.
The cargo cult list
Some tuning survives in forum posts long after it stopped mattering. Micro-tweaking PHP memory limits when your problem is query count. Chasing a PageSpeed score into the nineties on a page that already renders in 400ms while the checkout takes four seconds. Swapping the template engine cache directory to a RAM disk. Turning off features one by one hoping one of them was the culprit. All of it is motion, and none of it is measurement.
The discipline is dull and it works: measure the real page a real customer waits on, find the biggest number, remove it, measure again. Everything on this page is a hypothesis until your own profiler agrees. If someone quotes you for a performance project without asking to see a profile first, they are selling you a list, not a fix.
| Measure | Effort | Worth it when |
|---|---|---|
| Check HTTP cache hit rate | An afternoon | Always — do this before anything else |
| Narrow cache-tag invalidation | 1–3 days | Price or stock updates flush the whole cache |
| Fix N+1 in criteria | Days per hot page | Query count exceeds items shown |
| Move worker to CLI | Hours | Always in production |
| Add Elasticsearch | Weeks + ongoing ops | Tens of thousands of SKUs and heavy filtering |
- A cache that flushes on every price change is not a cache, it is a warm-up loop.
- The most common Shopware performance bug is a loop that queries per product.
- Bigger hardware multiplies your architecture — including its 240 queries.
- Elasticsearch is a permanent operational cost for a gain most catalogues never see.
Frequently asked questions
Because hardware multiplies whatever your code is doing. If a category page issues two hundred queries or the HTTP cache never hits, a faster machine does the same wasteful work slightly quicker. Profile one real page first: look at query count and cache hit rate. In our experience those two numbers explain most slow shops, and neither is fixed by a bigger instance.
Probably not below tens of thousands of products. It genuinely helps large, filter-heavy catalogues with real search traffic. Below that you are adding a second data store, a sync job and a new way for the shop to break, in exchange for a speed gain your customers cannot perceive. Measure your listing and search response times before you decide.
In production, yes. It ties background processing to somebody having the administration open in a browser, so your queue stalls overnight and your indexing piles up until it stampedes. Moving the consumer to a supervised CLI process takes hours and removes a whole category of mysterious evening behaviour. It is the cheapest fix on the entire list.
The diagnosis is one to two days and it tells you whether you have an afternoon of work or a month. Cache and worker issues are hours. Untangling N+1 queries in custom plugins is days per hot page. Anyone who quotes a fixed performance package before seeing a profile is selling you a checklist, and checklists do not know which of your pages is slow.
We do this for a living — Shopware, Node.js, React, ERP integration and automation for B2B.
Talk to an engineer