A slow WooCommerce filter that calls admin-ajax.php on every click boots WordPress on the server again. That call takes a PHP worker, a database query, and often 800 ms to 2,500 ms. On a catalog of tens of thousands of products it becomes high CPU or a timeout. After the first page load InstantFilter makes no further filter request: the click resolves in the browser, in 1.5 ms to 5 ms, with 0 SQL.
Why does every WooCommerce filter click call admin-ajax.php?
Open Chrome DevTools, the Network panel, filter on XHR or Fetch, and click a color or a size. A classic WooCommerce filter shows a POST to /wp-admin/admin-ajax.php, or a REST route along the lines of /wp-json/.... The status is 200 and the time is hundreds of milliseconds to a few seconds. That file is WordPress’s standard AJAX endpoint: a plugin registers an action, the browser posts that action, and WordPress loads so it can run the callback.
The first HTML of the category page can still be fast. The TTFB of /product-category/shoes/ says nothing about the click that follows. The shopper sees a spinner only after the page already looked finished. PageSpeed measures that first paint. The Network panel measures the filter. If you are searching for woocommerce admin-ajax.php slow, this is the row you are explaining: one request per filter click, not a slow homepage.
Sitewide search is a different job. A header box that finds a SKU across the whole store is not a facet inside a category. InstantFilter is the archive filter. It does not replace that search box. The request you see on the category archive is the filter. A sitewide search plugin can stay. It should not share the facet’s admin-ajax.php action, and the facet should not ride along on the header search request.
Why does admin-ajax.php show high CPU during filtering?
admin-ajax.php high cpu happens because the endpoint is not a thin JSON handler. WordPress boots plugins, the theme, and WooCommerce. The filter plugin then builds a WP_Query with a tax_query and often a meta_query. Counts next to each option add another query per term. On a shop with a handful of products that feels acceptable. On an archive with thousands of SKUs the same click becomes a CPU spike, because PHP and MySQL repeat the work for every shopper.
Host graphs show this as a row of short, expensive PHP processes rather than one heavy page load. Each click is a process. Ten shoppers ticking a size at the same time are ten processes. The query itself often shows up in Query Monitor as a join on term tables or on wp_postmeta. admin-ajax.php is only the door. The cost is whatever runs behind that door.
A larger VPS raises the ceiling. It does not remove the request. The click is still a cold boot of WordPress plus a query whose cost grows with the number of products, terms, and concurrent shoppers. That is why a store that filtered smoothly at 2,000 products suddenly shows red in the host panel at 15,000 products, while the category URL itself is still green in PageSpeed.
Why does a WooCommerce filter time out on admin-ajax.php?
A timeout is the same request hitting the PHP limit or the proxy limit. max_execution_time, a LiteSpeed or Nginx timeout, or the host’s 504: the browser waits, admin-ajax.php does not answer in time, and the filter stays on the spinner. In the Network panel you then see a failed or canceled request instead of a 200 that took two seconds.
On a catalog of 10,000, 20,000, or 50,000 products the query gets heavier. More variations, more terms, more counts. The filter that returned in 400 ms on a small category crosses the limit on the main archive. The number in the assortment is the situation in which the timeout appears. It is not the search phrase. The phrase you copy is woocommerce ajax filter timeout, or the filename admin-ajax.php.
Retry logic in the theme makes it worse. A failed click is sent again, two workers do the same work, and the host reports a stack of 502s or 504s around the filter. The category page itself stays reachable, because it comes from the page cache. Only the interaction after load goes through the timeout.
Why does caching admin-ajax.php not fix the filter?
A page cache stores the HTML of a URL. A filter click is not a repeat visit to that URL. It is a POST to admin-ajax.php, or a GET with a unique mix of color, size, and price. That mix is not in the category page cache. A Redis object cache can speed up a query if the plugin stores the result on purpose. The WordPress boot and the worker remain. A shop with many facets has more combinations than an object cache can usefully hold.
That is why a shop with WP Rocket, LiteSpeed, or Cloudflare is green on the first load and slow on the click. The longer version is the guide on filters that bypass the page cache. admin-ajax.php is almost always excluded from the page cache on purpose: the response depends on the logged-in user, the nonce, and the POST body. You cannot fold that endpoint into the HTML cache without changing how the filter works.
What does one request per click do on a large catalog?
Count the request, not the product total in the title. One shopper who sets three facets is three boots of WordPress. A category page with 40 concurrent visitors during a sale is 40 boots per extra click. Catalog size decides how expensive each boot is. Past a few thousand products the time sits in the query and the counts. Past ten or twenty thousand products the time also sits in the queue for a free worker. The hub article on why WooCommerce filters get slow describes that architecture. This page is the screen in front of it: the admin-ajax.php row in the Network panel.
A database index, such as the one FacetWP builds, often removes the heaviest join. The click is still a request. You still see admin-ajax.php or a custom endpoint, only with a shorter query. Network latency and the PHP boot remain. For a shop in a different country from the server, 200 ms of network is normal before PHP starts. A faster query does not remove those 200 ms.
How do you filter without a request to admin-ajax.php on every click?
The first page load stays server-rendered HTML, so Google still sees the category. During indexing InstantFilter turns the archive into a compressed codebook. After hydration the browser resolves the click locally. In the Network panel, no new filter request to admin-ajax.php appears after that load. The interaction sits in 1.5 ms to 5 ms, with 0 SQL per click. The difference between an AJAX architecture and that frontend layer is the guide on AJAX versus frontend filtering.
Check it on staging, not on a slogan. Turn the current filter on, count the XHR calls to admin-ajax.php per click, then repeat the same click with InstantFilter. The category URL can stay cached. The click should then be quiet in the Network panel. A catalog of 50,000 products is a load test for that check, not a keyword: either the request is gone, or it is not.
Keep going
The request in the Network panel belongs to an architecture. These pages go one layer deeper, or one step back to the hub.
- When the request gets blocked, not just slow
- The newer Blocks counting endpoint, and its own cap
- What that request's URL does to crawl budget
- Why WooCommerce filters get slow
- AJAX versus frontend filtering
- The query behind the request: wp_postmeta
- Why the cache does not save the filter click
- Filtering a catalog of 50,000 products
- Pricing and trial