A WooCommerce store that loaded fine at launch is crawling a year later. The hosting did not change and neither did the theme. What changed is the number of products, orders and plugins — and WooCommerce gets slow in predictable ways.
1. Plugins doing work on every page
The usual culprit. Thirty plugins is not automatically a problem; one badly written plugin running a query on every request is. Install Query Monitor, load a product page, and look at which plugin owns the slowest queries.
Watch for plugins that:
- load their scripts on every page instead of the pages they are used on
- write to
wp_optionson each request - add autoloaded options that grow without bound
Check that last one directly:
SELECT SUM(LENGTH(option_value))/1024/1024 AS mb
FROM wp_options WHERE autoload = 'yes';
Anything over about 1 MB is loaded on every single request. I have seen 40 MB from a plugin that never cleaned up after itself.
2. The postmeta table
WooCommerce stores product attributes, order details and much else in wp_postmeta. On a store with a few thousand products that table reaches millions of rows, and queries filtering by meta_key scan it.
Confirm the standard index exists — some migrations lose it:
SHOW INDEX FROM wp_postmeta;
You want one on meta_key. Then clear what should not be there:
DELETE pm FROM wp_postmeta pm
LEFT JOIN wp_posts p ON p.ID = pm.post_id
WHERE p.ID IS NULL;
That removes metadata belonging to posts that no longer exist. Back up before running it.
Turn on HPOS
Recent WooCommerce can store orders in dedicated tables instead of the posts table. On a store with order history it is a large win. WooCommerce → Settings → Advanced → Features → High-performance order storage.
3. Caching the wrong pages
Full-page caching is what makes WordPress fast, but cart, checkout and account pages must never be cached — a cached cart shows one customer another customer's basket.
Every caching plugin has an exclusion list. Make sure it contains /cart, /checkout, /my-account and anything with ?add-to-cart. Then test it: add a product in one browser, open the site in a private window, and confirm the cart is empty.
4. Cart fragments on every page
WooCommerce refreshes the mini-cart with an AJAX call, and by default it runs on every page — including ones with no cart on them. It is uncacheable and often the slowest request on a page.
add_action('wp_enqueue_scripts', function () {
if (! is_woocommerce() && ! is_cart() && ! is_checkout()) {
wp_dequeue_script('wc-cart-fragments');
}
}, 11);
If your header shows a live cart count everywhere, leave it enabled — that is the trade.
5. Images
Product photos uploaded straight from a camera are frequently 3–4 MB each. Twenty on a category page is 60 MB before anything else loads.
- Serve WebP
- Size them to what the theme displays, not the original dimensions
- Lazy load anything below the fold
- Set explicit width and height so the layout does not jump
This is usually the single biggest gain on a product page, and the easiest.
6. Scheduled tasks piling up
WordPress runs cron on page loads by default, so a visitor pays for whatever is due. On a busy store, disable that and run it from a real cron job:
// wp-config.php
define('DISABLE_WP_CRON', true);
*/5 * * * * curl -s https://yourstore.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
Also check WooCommerce → Status → Scheduled Actions. Tens of thousands of pending actions means something is failing and retrying forever.
Where to start
- Query Monitor on a product page — find the slowest queries and who owns them
- Check autoloaded options size
- Confirm cart, checkout and account are excluded from cache
- Compress and resize images
- Enable HPOS if the store has order history
Measure before and after each one. Changing five things at once tells you nothing about which mattered.