Een trage WooCommerce-filter die bij elke klik admin-ajax.php aanroept, start WordPress opnieuw op de server. Die aanroep kost een PHP-worker, een databasequery en vaak 800 ms tot 2.500 ms. Bij een catalogus van tienduizenden producten wordt dat hoog CPU-gebruik of een timeout. InstantFilter doet na de eerste pageload geen filter-request meer: de klik telt in de browser, in 1,5 ms tot 5 ms, met 0 SQL.
Waarom zie ik admin-ajax.php bij elke WooCommerce-filterklik?
Open Chrome DevTools, tabblad Netwerk, filter op XHR of Fetch, en klik een kleur of maat aan. Bij een klassiek WooCommerce-filter verschijnt een POST naar /wp-admin/admin-ajax.php, of een REST-route in de trant van /wp-json/.... De status is 200 en de tijd staat in honderden milliseconden tot een paar seconden. Dat bestand is het standaard AJAX-endpoint van WordPress: een plugin registreert een action, de browser post die action, en WordPress laadt om de callback te draaien.
De eerste HTML van de categoriepagina kan daarbij snel zijn. De TTFB van /product-categorie/schoenen/ zegt niets over de klik erna. De shopper ziet een spinner pas nadat de pagina al “klaar” was. PageSpeed meet die eerste paint. Het Network-tabblad meet de filter. Als je zoekt op woocommerce admin-ajax.php slow, is dit het scherm dat je aan het uitleggen bent: één request per filterklik, niet een trage homepage.
Het onderscheid met sitewide zoeken hoort hier ook. Een zoekbalk in de header die een SKU over de hele shop zoekt, is een andere taak dan een facet in een categorie. InstantFilter is het archieffilter. Het vervangt die zoekbalk niet. De request die je in het categorie-archief ziet, is de filter. Een plugin voor sitewide zoeken mag blijven. Die praat niet met de facet in de zijbalk, en de facet hoort niet via dezelfde admin-ajax.php-action te lopen als de headerzoekopdracht.
Waarom geeft admin-ajax.php een hoog CPU-gebruik bij een filter?
admin-ajax.php high cpu komt doordat het endpoint geen lichte JSON-handler is. WordPress bootst plugins, het theme en WooCommerce. Daarna bouwt de filterplugin een WP_Query met tax_query en vaak een meta_query. Voor de aantallen naast elke optie komt daar een telling per term bij. Op een shop met een handvol producten voelt dat acceptabel. Op een archief met duizenden SKU’s wordt dezelfde klik een CPU-piek, omdat PHP en MySQL het werk per bezoeker opnieuw doen.
Hostinggrafieken laten dat zien als een rij korte, dure PHP-processen in plaats van één zware pageload. Elke klik is een proces. Tien shoppers die tegelijk een maat aanvinken, zijn tien processen. De query zelf staat vaak in Query Monitor als een join op termtabellen of op wp_postmeta. admin-ajax.php is dan alleen de deur. De kosten zitten in wat er achter die deur gebeurt.
Een zwaardere VPS verhoogt het plafond. Het haalt de request niet weg. De klik blijft een cold start van WordPress plus een query waarvan de kost meegroeit met het aantal producten, termen en gelijktijdige shoppers. Dat is de reden dat een shop die op 2.000 producten soepel filterde, op 15.000 producten ineens rood staat in het hostingpanel, terwijl de categorie-URL zelf nog groen is in PageSpeed.
Waarom loopt een WooCommerce-filter op een timeout via admin-ajax.php?
Een timeout is dezelfde request die de PHP-limiet of de proxy-limiet raakt. max_execution_time, een LiteSpeed- of Nginx-timeout, of de 504 van de host: de browser wacht, admin-ajax.php antwoordt niet op tijd, en de filter blijft op de spinner staan. In het Network-tabblad zie je dan geen 200 van twee seconden, maar een failed of canceled request.
Bij een catalogus van 10.000, 20.000 of 50.000 producten wordt de query dieper. Meer variaties, meer termen, meer tellingen. De filter die op een kleine categorie in 400 ms terug was, zit op het hoofdarchief over de limiet. Het getal in de URL of in het assortiment is niet de zoekterm. Het is de situatie waarin die timeout verschijnt. De zoekterm blijft de request die je kopieert: woocommerce ajax filter timeout of de bestandsnaam admin-ajax.php.
Retry-logica in het theme maakt het erger. Een mislukte klik wordt opnieuw verstuurd, twee workers doen hetzelfde werk, en de host meldt een stapel 502’s of 504’s rond de filter. De categoriepagina zelf blijft bereikbaar, omdat die uit de paginacache komt. Alleen de interactie na de load gaat door de timeout. In het access-log zie je dan dezelfde action-naam twee of drie keer achter elkaar, met dezelfde filterwaarden.
Waarom helpt cachen van admin-ajax.php niet?
Paginacache bewaart de HTML van een URL. Een filterklik is geen herhaald bezoek aan die URL. Het is een POST naar admin-ajax.php, of een GET met een unieke combinatie van kleur, maat en prijs. Die combinatie zit niet in de cache van de categoriepagina. Redis object cache kan een query versnellen als de plugin het resultaat expliciet bewaart. De WordPress-boot en de worker blijven. En een shop met veel facetten heeft meer combinaties dan een object cache praktisch vasthoudt.
Daarom blijft een shop met WP Rocket, LiteSpeed of Cloudflare groen op de eerste load en traag op de klik. De uitleg staat in de gids over filters die de paginacache omzeilen. admin-ajax.php is vrijwel altijd uitgesloten van paginacache, met opzet: het antwoord hangt af van de ingelogde gebruiker, de nonce en de POST-body. Je kunt dat endpoint niet “even meesturen” in de HTML-cache zonder de filter te veranderen.
Wat doet één request per klik met een grote catalogus?
Tel de request, niet het productaantal in de titel. Eén shopper die drie facetten aanzet, is drie boots van WordPress. Een categoriepagina met 40 gelijktijdige bezoekers tijdens een sale is 40 boots per extra klik. De catalogusgrootte bepaalt hoe duur elke boot is. Voorbij een paar duizend producten zit de tijd in de query en de tellingen. Voorbij tien- of twintigduizend producten zit de tijd ook in de wachtrij voor een vrije worker. Het hoofdartikel over waarom WooCommerce-filters traag worden schetst die architectuur. Deze pagina is het scherm ervoor: de regel admin-ajax.php in het Network-tabblad.
Een index in de database, zoals FacetWP die bouwt, haalt de zwaarste join vaak weg. De klik blijft een request. Je ziet dan nog steeds admin-ajax.php of een eigen endpoint, alleen met een kortere query. De latency van het netwerk en de PHP-boot blijven. Op een shop in een ander land dan de server is 200 ms netwerk normaal, vóór PHP begint. Die 200 ms verdwijnt niet door een snellere query.
Hoe filter je zonder een request naar admin-ajax.php per klik?
De eerste pageload blijft server-side HTML, zodat Google de categorie ziet. Tijdens het indexeren zet InstantFilter het archief in een gecomprimeerd codebook. Na hydratatie lost de browser de klik lokaal op. In het Network-tabblad verschijnt na die load geen nieuwe filter-request naar admin-ajax.php. De interactie zit in 1,5 ms tot 5 ms, met 0 SQL per klik. Het verschil tussen een AJAX-architectuur en die frontend-laag staat in de gids AJAX versus frontend-filteren.
Controleer het op staging, niet op een belofte. Zet de huidige filter aan, tel de XHR naar admin-ajax.php per klik, en doe dezelfde klik daarna met InstantFilter. De categorie-URL mag gecachet blijven. De klik hoort daarna stil te zijn in het Network-tabblad. Een catalogus van 50.000 producten is daar een belastingstest voor, geen zoekwoord: de request verdwijnt, of hij verdwijnt niet.
Ga verder
De request in het Network-tabblad hoort bij een architectuur. Deze pagina’s gaan een laag dieper of een stap terug naar de hub.
- Waarom WooCommerce-filters traag worden
- AJAX versus frontend-filteren
- De query achter de request: wp_postmeta
- Waarom cache de filterklik niet redt
- Filteren op een catalogus van 50.000 producten
- Prijzen en proefperiode