A slow WooCommerce store is almost always slow on the server, not in the browser, and almost always for one of a short list of reasons: a database query that scans postmeta, an options table full of autoloaded data, plugins running on pages they have no business on, cart fragments, no object cache, orders still in the posts table, or hosting that was never sized for uncached traffic. Images come last. Each cause leaves a different fingerprint in the measurements, so you can identify yours before spending money on the wrong fix.
First, measure the right thing
Most speed advice starts with a PageSpeed score taken on the home page. The home page is cached. It tells you almost nothing about a store. Measure time to first byte on the pages that cannot be cached: the cart with an item in it, the checkout, the account page, and the orders screen in wp-admin. Open each with the browser's developer tools on the Network tab and read the waiting time on the main document request. Take the middle of three runs and compare it with a cached page. If the cached page is fast and the uncached pages are slow, the problem is on the server, and every item below is a candidate. If the cached page is slow as well, start with hosting.
Then install Query Monitor, on staging or briefly on the live site. It lists every database query on the page, how long each took, and which plugin or theme ran it. It also shows total query time, peak memory, whether an object cache is active, and any HTTP calls made during the request. Nearly every cause below shows up there.
1. Slow queries on postmeta
WooCommerce keeps products in the posts table and every attribute, price, stock value and custom field in postmeta as key-value rows. Filtering products by those values means joining postmeta to itself once per condition, and that gets slow as the table grows. Layered navigation, attribute filters, sorting large categories by price, and plugins that query products by custom fields are the usual triggers. WooCommerce added a lookup table for price and stock to take pressure off, but many filter plugins bypass it.
How to tell: in Query Monitor, sort queries by time. If the slowest ones are SELECTs that join wp_postmeta two or more times and they take longer than everything else combined, this is you. The fix is a rewritten query, an index, or a lookup table kept current by hook, depending on which query it is.
2. Autoloaded options and transient bloat
WordPress loads every option flagged autoload on every request, before anything else runs. Plugins that store settings, caches, logs or expired transients there push that payload into the megabytes, and the whole site pays for it on every page, cached or not, front or admin. Transients pile up in the options table on any site without a persistent object cache, and many plugins never clean theirs up.
How to tell: run wp option list with the autoload filter and total format over WP-CLI, or ask your host for the size of autoloaded options. Anything over a megabyte is worth cleaning. Several megabytes is a problem in its own right. The fix is deleting expired transients, switching autoload off for large options that do not need it, and finding which plugin keeps writing them.
3. Plugins that load on every page
A plugin written carelessly registers its scripts, styles, queries and API calls on every request rather than only where they are needed. A form builder loading its full library on the checkout. A shipping calculator running on the blog. An analytics plugin making a remote HTTP call before the page renders. Individually small, together the biggest share of server time on many stores.
How to tell: Query Monitor groups queries and HTTP calls by component. Look at the checkout page and ask, for each plugin listed, whether it has any reason to be there. Then deactivate the suspects one at a time on staging and re-measure the checkout TTFB. The fix is usually conditional loading, replacing the plugin, or removing it.
4. Cart fragments
Cart fragments is the AJAX request that refreshes the mini-cart so the item count is right on cached pages. It fires on every page load for every visitor, including people who will never add anything to the cart, and it is an uncached request that boots all of WordPress. On a busy store that is a lot of PHP worker time spent on a widget.
How to tell: watch the Network tab on a page like the blog for a request containing wc-ajax=get_refreshed_fragments. If it is there and it takes as long as the page itself, it is costing you. The fix is to dequeue it on pages without a cart, or replace the mini-cart with one that only fetches on interaction.
5. No object cache
Without a persistent object cache, every request rebuilds the same option, term and post lookups from the database. Redis or Memcached, connected through an object-cache.php drop-in, keeps those results in memory between requests. It is the single change that helps uncached pages most, and many hosts leave it off by default.
How to tell: Query Monitor's overview panel says whether an external object cache is in use. If it says no, and your uncached pages run hundreds of queries, this is a large part of the gap. The fix is enabling it at the host, then re-measuring, because a plugin that caches poorly will show up more clearly once the rest is fast.
6. Orders still in the posts table
Before High-Performance Order Storage, orders were posts and order data was postmeta, sharing tables with products and pages. Stores with tens of thousands of orders feel it in wp-admin first: the orders list, searches and reports crawl. HPOS moves orders into their own tables and has been the default for new stores since WooCommerce 8.2, but a store built before that stays on the old storage until someone migrates it.
How to tell: WooCommerce, Settings, Advanced, Features shows which storage is active. Also check whether compatibility mode is on, which writes every order to both places and doubles the cost. The fix is confirming every plugin that touches orders is HPOS-compatible, then migrating on staging before live.
7. Hosting that does not fit the store
Shared hosting with a small number of PHP workers, a database on a distant server, and no object cache will make even a clean store slow under real traffic. Bot traffic makes it worse, because bots hit uncached URLs with query strings and exhaust the workers.
How to tell: the cached home page is slow too, or TTFB swings wildly between runs, or the host's dashboard shows workers at capacity most of the day. The store has outgrown the plan. No code fix changes that.
8. Images and front-end weight
Large images, render-blocking scripts and unminified CSS are real, and they are what every generic guide covers, which is why they are last here. They affect how long a page takes to paint after the server has responded. On most stores we see, the server response is the larger number by a wide margin. Fix the server first, then the front end.
Why a caching plugin does not fix checkout
Page caching stores a rendered copy of a page and serves it to everyone. The cart, checkout and account pages are different for every visitor, so caching plugins exclude them, and they set a cookie when there is an item in the cart that bypasses the cache for that visitor on every page after that. The pages where money changes hands are never cached, and neither is wp-admin. Installing a caching plugin on a store that is slow at checkout speeds up the blog and leaves the checkout exactly as it was. The eight causes above are what change checkout speed.
Finding it and fixing it
The measuring described here takes an afternoon and tells you which of the eight you have. If you would rather hand it over, our WooCommerce speed optimization work starts with exactly this diagnosis and gives you the ranked list in writing before anything changes. Agencies that look after stores for clients can run the same diagnosis under their own name through our white-label maintenance service. Stores that only need the query and plugin fixes done can hire a WooCommerce developer for that piece alone.
Common questions
Is it safe to run Query Monitor on a live store?
Yes, for a short session. It only shows its panel to logged-in administrators and adds a small overhead while active. Take your readings, then deactivate it. For anything longer than a look, use staging.
Will more PHP workers fix a slow checkout?
Only if the checkout is slow because requests are queuing. If a single checkout request takes three seconds with nobody else on the site, more workers let more people wait three seconds at once. Fix the request time first, then size the workers for traffic.
Does the number of plugins matter?
Less than what each plugin does. Forty well-written plugins that load only where needed cost less than one that runs a heavy query on every request. Count the work, not the plugins.
Next step
If you have a TTFB number for your checkout and it is not one you like, send us the store URL and the number, and we will tell you which of these to look at first.
Want a senior developer to look at your site?
Tell us what you need. You get a written scope and a fixed price, usually within one business day.





