Why Are My WooCommerce Shop and Category Pages Slow?
On a large catalogue, a slow shop or category page is almost always the database doing far more work than the page needs: counting every matching product to print “page 1 of 5,000”, joining big taxonomy tables to work out which products belong to a category, and sorting the whole result before it hands back 20 products. On top of that, anything that differs per visitor (prices, a basket, a logged-in account) stops your page cache serving the page at all, so every visitor pays that cost in full.
The fix depends on which of those you have, so measure first and then work down the list below.
What makes catalogue pages slow
Counting every product for pagination. To print page numbers, WordPress counts every product that matches the page, not just the 20 it shows. On a category with tens of thousands of products, the count can cost more than fetching the page itself.
Taxonomy joins and de-duplication. WordPress finds the products in a category by joining wp_posts to wp_term_relationships, one of the largest tables on a big store. A product in two matching categories then appears twice, so WordPress adds a GROUP BY to remove duplicates, which sorts the whole match set first. Including subcategories makes the list of categories longer before the join even starts.
Sorting by price or another custom field. Sorting by price means reading _price from wp_postmeta for every matching product. Without a suitable index that is a scan.
Filter widgets. A sidebar that shows a count beside every attribute and category runs its own queries, often one per widget. On a big catalogue the filters can cost more than the products.
Bots crawling filter combinations. Every combination of filters is a unique URL, and crawlers will request thousands of them. Each one is a fresh, uncached page.
Per-visitor content defeats the page cache. A page cache serves one stored copy to everyone. If prices vary by customer, or the visitor has something in their basket, or is logged in, most caches skip the page, and the database work above runs for every one of those visitors.
Plugins loading where they do nothing. Every active plugin loads on every page by default, including ones that only matter on checkout or in wp-admin.
Find out which one you have
Profile the slow page rather than guessing. Super Speedy Performance Analysis is free. It profiles your key pages and attributes SQL time, rows, queries and memory to the individual plugin or theme responsible, so you can see whether the time goes on the product query, the filters, or something else entirely. Query Monitor shows the same queries for a single page view.
Test with your page cache bypassed, or logged out on a page the cache has not seen yet. A cached page can look fast while every customer with a basket waits on the uncached version.
Fixes, in the order to try them
1. Make the queries cheaper: Scalability Pro
Scalability Pro works on the queries WordPress and WooCommerce already run, without changing your theme:
- Custom Database Indexes give the slow lookups an indexed path.
- Remove SQL_CALC_FOUND_ROWS replaces the full count with a cheaper pagination model.
- Alter the main query to use EXISTS rather than LEFT JOIN, together with Optimise WooCommerce Group By in Advanced Archive Query Tweaks, removes the de-duplication step where the joins allow it.
- Unload Plugins stops plugins loading on the pages where Performance Analysis measured that they do nothing.
2. Take category lookups off the relationship table: Super Speedy Archives
Super Speedy Archives is an add-on for Scalability Pro or Super Speedy Filters. It keeps an ancestors table holding, for every product, every category above the one it is attached to. A category page then reads one indexed table instead of expanding the category tree and joining wp_term_relationships, so including subcategories costs nothing extra, and where the query allows it the join to wp_posts and its GROUP BY go entirely.
3. When the filters are the slow part: Super Speedy Filters
If profiling shows the time in your filter widgets, Super Speedy Filters replaces them. It filters and counts from its own ancestors table rather than wp_term_relationships, caches rendered widgets that cross a time threshold, and can defer a widget’s query until a visitor opens it. Its Archive Performance report shows how much of your archive traffic is bots, and on pages marked noindex it holds the filter work back until a real interaction, so bots crawling your filters get the cheap version of the page.
4. When per-visitor prices stop the page cache: Super Speedy Ajax Prices
If your prices vary by role, customer or rule, your page cache is probably switched off for the shop. Super Speedy Ajax Prices caches one copy of the page for everyone and fills in each visitor’s prices, stock and Add to Cart buttons with one batched request per page. See Choosing an Ajax Type for how that request is served.
5. When baskets and logged-in customers still miss the cache: Cache Everything for Everyone
Most page caches give up as soon as a shopper adds something to their basket or logs in, which is exactly when they are most likely to buy. Cache Everything for Everyone is a done-for-you service that sets up full-page caching for basket holders and logged-in customers too, with the checks that make it safe on your store.
On superspeedyplugins.com, my own store, a shopper with an item in their basket got cached pages with an 80 ms median time to first byte (3 requests, measured 13 August 2026). That figure depends mainly on the speed of the cache service and on where the visitor is, so I measure your store before and after rather than quote you mine.
Frequently asked questions
What filter speed should I expect on a small catalogue?
On a catalogue of a couple of thousand products, filtering that takes several seconds per click is not the catalogue’s size. The time is going somewhere specific: usually filter widgets running their own count queries, a page builder rendering heavy templates, or plugins that load on every page. Profile one filtered URL and the cause shows up in the per-plugin breakdown.
Will an object cache like Redis fix slow shop pages?
It helps, but it does not fix a slow query. An object cache stores the results of lookups WordPress repeats, so the second request for the same thing is cheaper. A shop page with a unique filter combination, or the first request after a product changes, still runs the full query. Set one up anyway, as described in Configuring Redis for WordPress, then fix the queries.
Is it my hosting?
Sometimes, but rarely only that. A faster server makes every query quicker by roughly the same factor, while the problems above make a query do far more work than it needs to. Measure first. If the slow time is spread evenly across everything, the server is the place to look, and Choosing WordPress Hosting for Performance covers what to look for.

Comments