Most advice about slow WooCommerce stores is the same list: install a caching plugin, buy a CDN, optimize images. That advice isn't wrong. It just skips the step that matters most: finding out what is actually slow on your store.
I've spent over nine years working on WordPress sites, many of them WooCommerce stores that arrived on my desk already slow. Below is the diagnosis process I use, in order, with the tools and queries I run. By the end you should be able to name the specific thing costing your store seconds, instead of guessing.
First, measure the right thing
Before touching anything, get two kinds of data.
Field data is what real visitors experience. Google collects it through Chrome and reports it in the CrUX dataset and Google Search Console. This is what Core Web Vitals rankings use. The thresholds, measured at the 75th percentile of visits, are:
- arrow_forwardLCP (Largest Contentful Paint): 2.5 seconds or less
- arrow_forwardINP (Interaction to Next Paint): 200 milliseconds or less
- arrow_forwardCLS (Cumulative Layout Shift): 0.1 or less
Lab data is a single test run from a tool like PageSpeed Insights, WebPageTest, or Lighthouse. It's repeatable, which makes it good for before/after comparisons, but it's not what Google ranks you on.
Check field data first at pagespeed.web.dev. If the "Real user experience" section shows a failing metric, that metric is your target. Then run a lab test on three URLs, not just the homepage:
- The homepage
- A product page
- The cart or checkout page
WooCommerce stores are frequently fast on the homepage and slow everywhere money changes hands, because product, cart, and checkout pages often bypass page caching. Testing only the homepage hides the real problem.
One more number to record before you start: TTFB (Time to First Byte) on each of those three pages. Everything else waits behind it. You can read it in the waterfall view of WebPageTest or in Chrome DevTools (Network tab, click the document request, look at "Waiting for server response").
The usual suspect: hosting and TTFB
If TTFB on an uncached page is above roughly 600ms, look at the server before anything else. No frontend optimization can recover time the server spends thinking.
WooCommerce is heavier than plain WordPress. Every product page runs pricing logic, stock checks, and session handling. Cart and checkout are dynamic by nature and cannot be served from page cache. That means raw PHP and database speed matter more for a store than for a blog.
Quick checks:
- arrow_forwardPHP version. Each major PHP release has brought measurable performance improvements. If the site runs PHP 7.4 (end of life since November 2022), upgrading to PHP 8.2 or 8.3 is the cheapest win available. Test on staging first, old plugins can break.
- arrow_forwardObject caching. Ask your host whether Redis or Memcached is available and whether a persistent object cache is active. WooCommerce makes many repeated database lookups per request; a persistent object cache absorbs them. WordPress Site Health (Tools, Site Health) will tell you if one is detected.
- arrow_forwardShared hosting ceilings. If the store is on a $5/month shared plan and does real traffic, no amount of tuning fixes contention with hundreds of neighbors. That's not a pitch for expensive hosting. It's arithmetic.
The WooCommerce-specific culprits
These are the problems generic speed guides miss because they are unique to WooCommerce.
Cart fragments (wc-ajax=get_refreshed_fragments)
Open DevTools, load any page on the store, and filter the Network tab for wc-ajax. You will likely see a request to ?wc-ajax=get_refreshed_fragments. This is the cart fragments script. Its job is to update the little cart icon in your header without a page reload.
The problem: it fires on every page load, it cannot be cached (it is personalized), and it forces WordPress to fully boot to answer it. On slow hosting, developers have reported this single request adding one to three seconds of load activity on every page, including pages where nobody is shopping.
Options, from least to most invasive:
- arrow_forwardMany performance plugins (Perfmatters, and others) have a "disable cart fragments" toggle.
- arrow_forwardDo it in code, but keep it active on cart and checkout where it is needed:
add_action( 'wp_enqueue_scripts', function () {
if ( is_cart() || is_checkout() ) {
return;
}
wp_dequeue_script( 'wc-cart-fragments' );
}, 11 );Trade-off to know: with fragments dequeued, the header cart count will not live-update when a user adds a product from a cached page. Test your theme's add-to-cart flow after the change. For most catalog-style stores the trade is worth it; for stores with heavy cross-page cart interaction it may not be.
admin-ajax.php pileups
Filter the Network tab for admin-ajax.php. Every request you see boots WordPress fully and skips page cache. Cart fragments is one client of it, but plugins abuse it too: live search, wishlists, popups, view counters. Three or four plugins each making an admin-ajax call per page view multiplies your server load. Note which plugin each request belongs to (the action parameter in the request payload usually names it), then decide whether the feature earns its cost.
Session and nonce behavior versus page cache
WooCommerce sets a session cookie once a visitor's cart has contents. Most page caches correctly bypass caching for those visitors. That is fine. The failure mode is the opposite one: a misconfigured cache that serves cached pages containing another user's cart HTML or a stale nonce, which then breaks add-to-cart. If customers report "please refresh and try again" errors on checkout, check the cache exclusion rules for /cart, /checkout, /my-account, and the WooCommerce cookies before blaming anything else.
Database bloat: autoloaded options
This one is invisible in any frontend tool, and I find it on most stores more than a few years old.
WordPress stores settings in the wp_options table. Every option marked autoload is loaded on every single request, admin and frontend, cached or not. Plugins add autoloaded options and, when uninstalled, often leave them behind. Over years, stores accumulate megabytes of dead data that PHP loads and parses thousands of times a day.
Measure it:
SELECT ROUND(SUM(LENGTH(option_value)) / 1024) AS autoload_kb
FROM wp_options
WHERE autoload IN ('yes', 'on', 'auto', 'auto-on');Or with WP-CLI:
wp option list --autoload=on --format=count
wp doctor check autoload-options-size # if wp doctor is installedWordPress Site Health warns when autoloaded data could affect performance. As a rule of thumb from hosting providers, staying under roughly 800KB is sensible; well-maintained sites are often far below that. Since WordPress 6.6, core refuses to autoload any single option larger than 150KB by default, which helps new bloat but does nothing for what is already there.
Find the heaviest offenders:
SELECT option_name, ROUND(LENGTH(option_value) / 1024) AS size_kb
FROM wp_options
WHERE autoload IN ('yes', 'on', 'auto', 'auto-on')
ORDER BY LENGTH(option_value) DESC
LIMIT 20;Typical finds: settings from plugins removed years ago, logging data a plugin wrote into options, and expired transients that never got cleaned up. For options belonging to plugins that are still installed, do not delete; flip autoload off instead:
UPDATE wp_options SET autoload = 'no'
WHERE option_name = 'the_offending_option';Back up the database before touching it, and change one thing at a time.
While you are in the database: wp_postmeta on WooCommerce stores grows large because every order and product variation writes rows there. If product searches or filtered category pages are slow, the queries against that table are worth profiling. I wrote separately about scaling WordPress search on large datasets in my post on WordPress fulltext search without Elasticsearch; the same profiling mindset applies here.
Plugin autopsy: find the one plugin costing you seconds
Install Query Monitor (free, and safe to run briefly on production for an admin user, though I prefer staging). Load your slowest page while logged in. Query Monitor shows:
- arrow_forwardTotal database queries and total query time, grouped by plugin
- arrow_forwardDuplicate queries (the same query run repeatedly in one request)
- arrow_forwardSlow queries with their full SQL and the code path that triggered them
- arrow_forwardHTTP API calls, which reveal a nasty class of problem: plugins calling external services synchronously during page load. A license check or a shipping-rate API with a two-second response adds two seconds to your page, full stop.
Sort by component. In my experience one or two plugins usually account for the majority of query time. Common repeat offenders on stores: related-products plugins doing unindexed lookups, badge/label plugins scanning all products, and analytics plugins writing to the database on every page view.
For a controlled experiment, copy the site to staging and use the Health Check & Troubleshooting plugin to disable everything except WooCommerce, then re-enable plugins one at a time, running the same lab test after each. It's slow and boring. It also ends arguments about which plugin is the problem.
Images and frontend weight
Now the familiar advice, which matters once the backend is honest:
- arrow_forwardServe modern formats. WebP support is universal in current browsers; AVIF support is now also broad. Most image optimization plugins or a CDN can convert on the fly.
- arrow_forwardSize images to their container. A 2000px product photo displayed at 400px wastes bandwidth on every visit. WordPress generates multiple sizes; make sure the theme actually uses
srcset(WordPress adds it by default since 4.4 unless the theme circumvents it). - arrow_forwardLazy-load below-the-fold images only. WordPress lazy-loads by default, but the LCP image (usually the product hero) must NOT be lazy-loaded, and should have
fetchpriority="high". WordPress 6.3+ attempts this automatically; verify in the page source that your main product image has noloading="lazy"attribute. - arrow_forwardAudit third-party scripts. Chat widgets, heatmaps, three analytics tools, a Facebook pixel, and TikTok tracking add up. Each one costs main-thread time, which is what INP measures. Remove what nobody reads the reports from.
What actually moved the needle: a priority order
When I run this process on client stores, the fixes that most often produce the largest measured improvements, in rough order of impact for effort:
- PHP upgrade plus persistent object cache (host-level, low risk)
- Cart fragments dequeued outside cart/checkout
- Autoload cleanup and database pruning
- Removing or replacing one or two heavyweight plugins identified by Query Monitor
- Image format, sizing, and LCP priority fixes
- Page caching and CDN configuration (last, because it hides backend problems if done first)
Notice that the standard advice (caching, CDN) is at the bottom. Caching makes a fast site scale. It does not make a slow site fast, because your logged-in users, cart pages, and checkout still hit the uncached path. Fix the uncached path first.
This diagnosis-first process is exactly what my WordPress speed optimization service delivers, with before/after Core Web Vitals data on every engagement.
Pitfalls: fixes that backfire
- arrow_forwardStacking optimization plugins. Two caching plugins, or a caching plugin plus host-level caching plus Cloudflare APO, fight each other. Pick one page cache. Purge everything when debugging.
- arrow_forwardDeferring or delaying all JavaScript. Aggressive "delay JS until interaction" settings can break add-to-cart, checkout fields, and payment gateways in ways that only appear for real customers. If you use these settings, exclude WooCommerce scripts and test a full purchase.
- arrow_forwardDeleting options you cannot identify. Flip autoload to
noinstead of deleting when unsure. Deletion is forever; a missed setting can silently break a plugin. - arrow_forwardTrusting one lab run. Lab scores vary run to run. Take three runs, use the median, and judge success by field data over the following weeks.
- arrow_forwardOptimizing the homepage only. Revenue happens on product and checkout pages. Measure those.
When to bring in help
You can do everything above yourself with free tools. The honest cutoffs where I would hire someone: the problem sits in a slow custom query or plugin you depend on and cannot replace, the store does enough revenue that a day of downtime costs more than a professional engagement, or you have applied the standard fixes and field data still fails Core Web Vitals after 28 days.
If that is where you are, I offer a WordPress speed optimization service built around this exact diagnostic process, and I work remotely with stores in Australia, Europe, and the US from Sri Lanka. You get a written diagnosis with numbers before any changes are made, so you know what you are paying to fix.
