It's Wednesday afternoon. A friend tells you she tried to check out on your store and it took "forever". You open the site, click around, and yeah, it's sluggish. You ping your agency. They tell you what they told you last quarter: you've got Varnish. You've got Redis. Hardware's fine. It shouldn't be slow.
But it is. And the invoice for that hardware is sitting in your inbox.
Key takeaway: You paid for Varnish. You paid for Redis. The store still feels slow. You're not imagining it, and it's almost never the hardware. It's usually one of three very specific things, and all of them are fixable in days, not months.
The 60-second version
If your Magento 2 store feels slow despite Varnish and Redis, it's usually because one of these is true:
- Varnish thinks your pages aren't cacheable, so almost every visitor hits PHP and the database. Hit rates under 30% are common on poorly configured stores. Healthy is 80% or higher.
- Redis is serializing customer requests, so the second tab a visitor opens has to wait for the first one to finish.
- Cron is jammed behind a single long-running job, so product updates, abandoned-cart emails, and stock syncs quietly stop happening.
None of these show up on a CPU graph. They show up as "the site feels off", lost sessions, and support tickets.
But I'm on Varnish and Redis, why is it still slow?
Varnish and Redis aren't switches. They're tools. You can own a very nice oven and still burn dinner if the dials are wrong.
Magento 2 ships with both enabled, but the default configuration assumes your theme, your extensions, and your third-party scripts all play nicely. In real life, one bad cookie or one chatty extension quietly breaks the whole thing, and nobody notices because the site still loads. Just slowly. On every request. Forever.
So when someone tells you "Varnish is on", that's a yes/no answer to the wrong question. The question is: is Varnish actually serving most of your traffic, or is it sitting there watching PHP do all the work?
Cause 1: a cookie is telling Varnish "don't cache this"
Varnish's job is simple. A visitor asks for /mens-shoes. Varnish checks if it already has a copy. If yes, it sends that copy in milliseconds without bothering PHP. If no, it fetches a fresh one, stores it, and serves the next visitor from cache.
The catch: Varnish won't cache a response that looks "personal". And Magento 2, plus a lot of extensions, love to make responses look personal when they don't need to.
The usual culprits:
- A cookie-consent extension that sets a cookie on the very first request, before the visitor has even clicked anything. Varnish sees the cookie, assumes the page is personalized, and refuses to cache it.
- A tracking or marketing extension that starts a PHP session on every page so it can "track" users, including bots.
- A promotional banner or geo-IP module that wants to know the customer group before the page renders.
Any one of these turns a fast category page into a full Magento request. Every. Single. Time. I've seen stores where the majority of traffic bypassed Varnish for exactly this reason. The hardware looked bored. The visitors did not.
How to spot it without touching code: open your store in a private browser window. Open the network tab (right-click, Inspect, Network). Load the homepage. Click any category. Look at the response headers for the HTML document. If you see Cache-Control: no-store or no-cache, or a Set-Cookie line on a guest page, Varnish is being told not to cache. That's the first domino.
You can also hand your developer this one-liner. It'll tell them in one go whether Varnish is actually doing its job:
1varnishstat -1 | grep -E 'cache_hit|cache_miss'
If misses massively outnumber hits, Cause 1 is live.
Cause 2: your Redis sessions are making customers wait in line
This one's subtle, and nobody catches it, including experienced teams.
Magento uses Redis to store customer sessions: the stuff that makes a cart remember what's in it, a login stay logged in, a checkout remember which step you're on. By default, Magento locks the session while one request is being processed so two requests don't trample each other's data.
Modern storefronts fire off five, ten, sometimes thirty requests at once when a customer loads a page. Background tabs. AJAX calls for mini-cart. Tracking pixels that touch the session. A promotional banner that starts a session to check the customer group. All of them queue up behind the first one, waiting for the lock.
On a healthy site, this is milliseconds of friction. On a site with a chatty extension that starts sessions it doesn't need, you get session lock contention: customers sitting on a spinner for 2 to 5 seconds on checkout, for no visible reason. No server alarm fires. No log entry screams. The CPU graph is flat. And the customer bounces.
This is the number one performance issue I've diagnosed on otherwise-healthy Magento stores in the last year. And it's the one that most commonly survives a round of "performance fixes" untouched, because unless you know to look for it, it hides.
How to spot it without touching code: ask your developer, "Are we using Redis session locking on the defaults, and which extensions start sessions on the storefront?" If the answer is "uhh", you've probably found it.
Cause 3: cron got stuck, and now other jobs aren't running
Cron is Magento's background worker. It's the thing that sends abandoned-cart emails, updates product prices from your ERP, cleans up stale data, refreshes the sitemap, and syncs stock. When cron is happy, you never think about it. When cron is broken, your business dies in slow motion.
Here's the failure mode: one cron job, usually from an old integration with an ERP, CRM, or marketing tool, runs way longer than it should. Five minutes. Ten minutes. I've seen one take over eight minutes, every run. Magento's cron system is polite. It waits for one job to finish before starting the next. So that one long job blocks everything behind it.
The result, on a real store I've looked at recently: over 90% of scheduled jobs were skipped. Not "delayed". Not "slow". Skipped entirely. Meaning prices weren't updating. Sales emails weren't sending. Stock wasn't syncing. The merchant had no idea, because nothing was "broken" in the visible sense. The site loaded. Checkout worked. But the background of the business had quietly stopped turning.
You don't feel this as slowness the way you feel Cause 1 and Cause 2. You feel it as: "Why didn't this customer get their order confirmation?" "Why is this product still showing in stock when I sold out yesterday?" "Why did our newsletter segment get skipped?" It's performance of a different kind. The store isn't slow, the business is.
How to spot it without touching code: ask your developer for the count of missed cron jobs over the last seven days. If it's more than a handful, something's blocking the queue.
What to ask your developer
Copy-paste this into a ticket. No technical knowledge needed on your end.
- What's our Varnish hit rate on guest traffic over the last 7 days? If it's under 80%, what's blocking it?
- Do any storefront extensions start a PHP session on guest pages? If yes, why?
- Is Redis session locking configured intentionally, or is it on the default? Can we measure lock wait time during checkout?
- How many cron jobs were missed in the last 7 days? Is any single job running longer than 60 seconds?
Four questions. If your team can answer all four confidently, you're probably fine. If they can't, that's your weekend project.
Your developer should also read the companion article: How to diagnose a low Varnish hit rate on Magento 2. It's the technical version of Cause 1 with the exact commands and config they'll need.
FAQ
Not sure which of the three is biting you?
We run a focused 3-day performance audit on Magento 2 stores. You get a plain-English report, the exact Varnish hit rate, cron miss count, and session lock data, and a prioritized list of what to fix and in what order. No jargon, no 80-page PDF nobody reads.