---
title: "WooCommerce Performance Testing Guide | Evaluat"
description: "WooCommerce is self-hosted, so unlike Shopify you can load test it. Why cart, checkout, and admin-ajax slow under load, and how to test a staging copy safely."
url: "https://www.evaluat.com/blog/woocommerce-performance-testing"
last_modified: "2026-06-27"
---

[Blog](https://www.evaluat.com/blog) [Ecommerce Performance](https://www.evaluat.com/blog/category/ecommerce-performance)

# WooCommerce performance testing: find your store's limit under load

WooCommerce runs your store, which means the slow paths are yours to fix. Its cached catalog flies, but cart, checkout, and my-account run PHP and MySQL on every request, and on budget hosting a thin worker pool caps how many shoppers that path can hold. Here is why it slows under load, and how to test a staging copy.

Written by: [Ahmad Farzan](https://www.evaluat.com/authors/ahmad-farzan) · 27 June 2026

![On a WooCommerce store, the cached catalog is served straight from cache, but cart and checkout run the full uncacheable path on every request: WordPress boot, autoloaded options, plugin hooks and queries, a PHP worker, and MySQL.](https://www.evaluat.com/blog/woocommerce-performance-testing-cover.svg)

Summary

WooCommerce scales, but not evenly. The cached catalog can serve thousands of visitors cheaply, while the pages shoppers spend money on, the cart, the checkout, and the account pages, are non-cacheable by design: they run PHP and MySQL on every request. Think of a printed menu that works for the whole room while every order still goes to a kitchen with a fixed number of cooks. On budget hosting that kitchen is a small pool of PHP workers, often with no persistent object cache, and every request carries the weight of autoloaded options, plugin hooks, and the mini-cart's admin-ajax calls. A normal day hides this because most visitors browse cached pages. A sale changes both numbers at once: more people arrive, and more of them head for checkout, so requests queue, response times climb, and the slowest time out as errors. That's also why your last load test may have said everything was fine: pointing traffic at the homepage just tests the cache, and an HTTP-only test never runs the theme and plugin JavaScript or records Core Web Vitals like Largest Contentful Paint. Because WooCommerce is self-hosted, you can test the whole journey, including checkout, unlike a hosted platform. So test a staging copy that mirrors production, check your host's terms first, script the full journey from product page to sandboxed checkout with unique data per virtual user, run it in a real browser, and ramp past expected peak until you find the concurrency where checkout stops holding. Then re-test after every plugin change.

Listen to this article · 1:51

[Download the audio summary (MP3)](https://www.evaluat.com/blog/audio/woocommerce-performance-testing.mp3)

Key takeaways

- WooCommerce cart and checkout are non-cacheable by default, so peak load hits the origin while the catalog can still look fast.
- Plugin stacks and shared hosting limits show up when many unique carts run together, not on a quiet single-user preview.
- Rehearse browse-to-checkout in real browsers before a sale; homepage-only HTTP checks miss the path that takes the money.

## Doesn’t WooCommerce scale? It runs on WordPress.

Yes, with the right hosting and caching, and the cached catalog scales cheaply. The trap is that the parts shoppers spend money on, the cart, the checkout, and the my-account pages, are non-cacheable by design. They run PHP and MySQL on every request while the homepage is served from a stored copy.

WooCommerce powers more stores than any other ecommerce platform. It rides on WordPress, which runs [41.5% of all websites](https://w3techs.com/technologies/details/cm-wordpress), and it accounts for [48.6% of every ecommerce system W3Techs tracks](https://w3techs.com/technologies/details/cm-woocommerce), with [more than 4.2 million live stores](https://storeleads.app/reports/woocommerce) as of June 2026. Most of those are small and mid-sized merchants on shared or managed WordPress hosting, which is exactly the audience this guide is for. Shopify leads among the largest, highest-traffic sites; WooCommerce owns the long tail.

And it scales. WooCommerce’s own [scaling guidance](https://woocommerce.com/document/woocommerce-scaling-faqs/) is blunt about it: “The sky is the limit. We’ve seen instances of shops with 100,000+ products listed, handling thousands of transactions per minute.” The four things that decide whether yours does are traffic, WooCommerce’s own code, the rest of your plugin and theme code, and your server hardware. Every one of those is a lever you control. WooCommerce is slow by default and fast when tuned, which is the whole opportunity: the store you assembled is yours to fix.

So where does it slow down? On the paths a cache cannot help. WooCommerce marks the cart, checkout, and my-account pages as non-cacheable by default, and supported caching plugins honor that exclusion, because those pages show one shopper their own cart and cannot be shared ([WooCommerce docs](https://developer.woocommerce.com/docs/best-practices/performance/configuring-caching-plugins/)). A printed menu board works for the whole room, but every order still goes to a kitchen that cooks it from scratch. Your catalog is the menu board. Checkout is the kitchen, and the kitchen has a fixed number of cooks. This is the same pattern that takes down [Magento checkout under load](https://www.evaluat.com/blog/magento-checkout-slow-under-load), scaled down to a WordPress store, and seeing whether yours holds takes a [real-browser performance test](https://www.evaluat.com/product/performance-testing), not a check of the homepage.

## What actually runs on every WooCommerce request?

On the uncacheable paths, every request boots WordPress, reads the full set of autoloaded options, runs each active plugin’s hooks and queries, and builds the page in PHP. On budget hosting a handful of workers handle all of it, often with no object cache, so the database takes every uncached query directly. Here is what stacks up.

### The cart-fragments AJAX call

WooCommerce refreshes the mini-cart with a script that, in its own words, “uses the WordPress admin-ajax API to update the cart without refreshing the page.” That call is uncached and handled by PHP. It used to be the textbook WooCommerce performance drag because, [before WooCommerce 7.8](https://developer.woocommerce.com/2023/06/16/best-practices-for-the-use-of-the-cart-fragments-api/), “the cart fragments script was enqueued on every page load … even if the mini-cart widget wasn’t present.” Since version 7.8 in June 2023 it loads by default only where the cart widget is actually rendered. The lesson is not that it is solved everywhere, because plenty of themes still render that widget on every page, and where the call fires it is origin work that multiplies with concurrency.

### Autoloaded options in wp\_options

WordPress keeps site settings in a table called wp\_options, and every option flagged to autoload is read [on each page request](https://developer.wordpress.org/advanced-administration/performance/optimization/). Plugins add to it, and expired transients, temporary cached values, often linger there long after they are useful. WordPress is explicit about the cost: without a persistent object cache, “your web server must read those options from the database to handle every page view.” On a busy uncacheable path, a bloated options table is a tax paid on every single request.

### Plugin sprawl

This is not about any one plugin. Each active plugin can add database queries, hook callbacks, and front-end scripts, and a store assembled from a dozen or more pays that cost on every request. WooCommerce names the rest of your plugin and theme code as one of the four things that decide whether you scale, and it is the one that grows quietly as a store ages. The fix is to measure which plugins cost the most, not to guess.

### A small worker pool and no object cache

The ceiling is set by how many requests your server can run at once. PHP processes requests through a pool of workers, and budget shared or managed hosting often gives a site only a handful. When every worker is busy building an uncacheable checkout page, the next request waits, and past a point the server returns 502 or 503 errors. A persistent object cache (Redis or Memcached) would keep repeated queries in memory, but WordPress is clear that this depends on the host: “For your site to use persistent object caching, your hosting provider must offer you a particular type of server, a cache server.” Without one, every uncached query falls straight through to MySQL.

### The front end shoppers actually feel

All of that is the server. The shopper also waits on the theme and plugin JavaScript that runs in their browser, plus any tracking tags, before the page is usable. This is where WooCommerce’s real-world numbers show the strain. Only [45% of WordPress sites pass all three Core Web Vitals on mobile](https://almanac.httparchive.org/en/2025/cms), and the weakest of the three is loading: just 53% have a good Largest Contentful Paint, per the 2025 Web Almanac. The median page across all websites now ships [2,164 KB, including 646 KB of JavaScript](https://almanac.httparchive.org/en/2025/page-weight), and a commerce store with a page builder and a dozen plugins tends to sit heavier. Those Vitals are measured in a browser, which is the first hint about why a server-only load test misses them. For how they move at concurrency, see [Core Web Vitals at load](https://www.evaluat.com/blog/core-web-vitals-load-testing).

## Why does it only fall over under load?

Because the cheap path and the expensive path scale in opposite directions. The cached catalog barely notices a crowd. The uncacheable cart-and-checkout path lands on a fixed pool of PHP workers, and when more shoppers transact at once than there are workers to serve them, requests queue, response times climb, and the slowest ones time out as 502s and 503s.

A normal day hides this. Most visitors browse cached pages, a few buy, and the worker pool is never full. A sale changes both numbers at once: more visitors arrive, and a larger share of them head for the cart and checkout, the one place the cache cannot help. The catalog still flies while the kitchen falls behind. Third-party work makes it worse, because a checkout often calls out to a payment gateway or a shipping or tax service, and a worker waiting on a slow external response is a worker serving no one else. Your sale peak is their peak too. This is the Magento checkout pattern on a smaller stack: the platform differs, the bottleneck shape does not. The Magento-scale version of the rehearsal is covered under [Magento load testing](https://www.evaluat.com/solutions/magento-performance-testing).

![A diagram of WooCommerce under load. The cached catalog is a wide green flow that scales freely, while the uncacheable cart, checkout, and admin-ajax requests funnel into a small fixed pool of PHP workers that fills up and overflows into queued requests and 502 errors.](https://www.evaluat.com/blog/woocommerce-performance-testing-workers.svg)

## Why did your load test say everything was fine?

Because it probably tested the cache. Pointing virtual users at the homepage measures how fast a stored copy is served, a number no WordPress site struggles with. The uncached checkout, the admin-ajax calls, and the theme and plugin JavaScript only show up when the test logs in, adds to cart, and renders the page in a real browser.

A virtual user is one simulated shopper the test drives. For the test to exercise the path that breaks, each one needs its own session and cart, and the test has to run the journey, not hammer a single URL. The choice of tool matters here too. Protocol tools like [k6](https://www.evaluat.com/vs) and JMeter are genuinely the right instrument for API and protocol concurrency, where there is no browser to run and a real one would be wasted overhead, and k6 even ships a browser module. But in HTTP mode they send requests and read the response bytes without executing the page’s JavaScript or rendering it, so the theme and plugin cost, the Core Web Vitals, and the admin-ajax behavior a browser would trigger are outside what they measure. That is the gap between what the server returns and [what users see, not what scripts pretend](https://www.evaluat.com/blog/api-vs-browser-performance-testing).

## What an HTTP test sees versus what a real browser sees

The two answer different questions. An HTTP test answers “did the server respond,” which on a cached WooCommerce catalog is almost always yes. A real-browser test answers “did the page stay usable for the shopper at peak,” which is the question your revenue turns on. The table maps what each one records during one request.

| During one WooCommerce request                | HTTP load test (HTTP mode)      | Real-browser test                       |
| --------------------------------------------- | ------------------------------- | --------------------------------------- |
| Server returns the cached catalog HTML        | Measured, and fast              | Measured, and fast                      |
| Uncached cart / checkout PHP path             | Hit, if scripted                | Hit, the way a shopper hits it          |
| Theme and plugin JavaScript parse and execute | Not run                         | Measured                                |
| admin-ajax cart-fragment behavior             | Not triggered as a browser does | Triggered and measured                  |
| Largest Contentful Paint and INP              | Not produced                    | Captured per session                    |
| Evidence of why one session stalled           | None                            | Session video, network log, console log |

To see the layer that breaks, the test has to run the page the way a shopper’s device runs it. That is [real-browser load testing](https://www.evaluat.com/blog/real-browser-load-testing): every virtual user is a real browser.

## How to load test your WooCommerce store (on staging)

Because WooCommerce is self-hosted, you can test the whole journey, including checkout, which is the opposite of a hosted platform like [Shopify](https://www.evaluat.com/blog/shopify-store-slow-under-load), where the checkout is sandboxed and off-limits. Do it against a staging copy that mirrors production, with payments in sandbox mode. The goal is one number: the concurrency at which checkout stops meeting your targets.

1. **Test a staging copy, and check your host’s terms first.** Only load test infrastructure you own or have permission to test, and use a staging or production-replica environment, not the live store. Many managed hosts restrict it. Kinsta, for example, states plainly that “load testing is prohibited on the MyKinsta platform” and points to its terms. A test against a shared box also measures the neighbours you share it with, so a copy on infrastructure that matches production is the honest target.
2. **Script the full journey, not a URL.** Product page, add to cart (which fires the fragments call), cart, checkout, with the payment gateway in sandbox. The journey is what shoppers do, and the journey is what breaks.
3. **Give every virtual user its own data.** A unique session, cart, and account per user keeps caching and database contention close to a real crowd. Hundreds of users sharing one cart collapse onto a single row and flatter the result.
4. **Find the worker-pool ceiling on purpose.** Ramp virtual users past your expected peak until error rate and p95 response time climb together. That crossover is your concurrency ceiling, and the method is the same as [stress testing a website](https://www.evaluat.com/blog/stress-testing-a-website). A flash sale or product drop, where the load lands in minutes, is the sharper case covered in [spike testing](https://www.evaluat.com/blog/what-is-spike-testing).
5. **Read the results per step, in a real browser.** A p95 spike on checkout points at PHP and MySQL; Largest Contentful Paint and INP drifting upward point at theme and plugin JavaScript. Modern stores keep order data in dedicated tables ([High-Performance Order Storage](https://developer.woocommerce.com/docs/features/high-performance-order-storage/), the default since WooCommerce 8.2 in October 2023), which eases order-write contention, but cart and checkout still run uncached PHP on every request, so the path that breaks first is unchanged.
6. **Re-test after plugin and theme changes.** Every added plugin, tag, or theme update moves the ceiling. The margin is worth real money: Google and Deloitte’s 2020 study [Milliseconds Make Millions](https://web.dev/case-studies/milliseconds-make-millions) found a 0.1 second mobile speedup lifted retail conversions by 8.4%.

## How Evaluat tests a WooCommerce store under load

Evaluat is a real-browser performance testing platform: every virtual user is a real, isolated browser that walks your store the way a shopper would, capturing Core Web Vitals under load along with session video, network logs, and console logs for each user. You build the journey once in a visual scenario editor, with no scripting.

Datasets feed each virtual user its own account, cart, and search terms; popup handlers dismiss the cookie and consent banners every storefront carries; and tests run from the region closest to your customers. When checkout gives way at peak, the report shows where. The per-URL view separates the catalog pages that held from the checkout step that did not, and the network log of any session shows which call stalled, a fragment refresh, a plugin’s admin-ajax handler, a payment redirect. Aggregate percentiles tell you something broke; they do not tell you who or why. A failure at peak isn’t a percentile. It’s a session. Open the session. Watch the moment it broke.

Two honest boundaries. Evaluat watches your store from the outside: it shows which request was slow and what the shopper experienced, but finding the slow query inside MySQL or the bloated autoload table still takes a server-side profiler like Query Monitor. And for raw protocol concurrency, thousands of API requests per second with no rendering, an HTTP tool like k6 or JMeter is the lighter, cheaper instrument. The real-browser model earns its cost on the JavaScript-heavy, uncacheable path this guide is about.

## Test the store you assembled, not the platform underneath

The asymmetry is the lesson. WooCommerce gives you a catalog that caches cheaply and a checkout that pays full price for every click, and because the whole stack is yours, the ceiling is yours to find and raise. Stop reading “WooCommerce scales” as proof that your store will. Script the journey that takes the money, run it in a real browser on a staging copy at the load you expect, and find the concurrency where it stops holding, before a sale finds it for you.

Related commercial pages: [ecommerce performance testing](https://www.evaluat.com/solutions/ecommerce-performance-testing), [checkout under load](https://www.evaluat.com/use-cases/checkout-performance-testing), and [peak readiness](https://www.evaluat.com/use-cases/peak-readiness).

Test in real browsers. Debug in real sessions. [Book a demo](https://www.evaluat.com/demo?intent=performance).

![Ahmad Farzan, Founder at Evaluat](https://www.evaluat.com/authors/ahmad-farzan.jpg)

About the author

[Ahmad Farzan](https://www.evaluat.com/authors/ahmad-farzan) · Founder at Evaluat

Founder of Evaluat. Has spent years building and load-testing Adobe Commerce and Magento storefronts, and built Evaluat to test sites the way real browsers actually hit them.

[Ahmad Farzan on LinkedIn](https://www.linkedin.com/in/ahmadfarzan/) [Ahmad Farzan on GitHub](https://github.com/farzanahmad)

[More from Ahmad →](https://www.evaluat.com/authors/ahmad-farzan)

Common questions

## FAQ

### Why is my WooCommerce checkout so slow?

Cart, checkout, and my-account are non-cacheable by design, so they run PHP and MySQL on every request while the catalog is served from cache. On budget hosting, a small PHP worker pool and no persistent object cache make that path the first to slow under load. It is driven by hosting, caching, and plugins, all of which you control.

### Does WooCommerce slow down WordPress?

It adds uncacheable paths and more plugin code, but WooCommerce scales with the right hosting, caching, and a disciplined plugin set. WooCommerce's own guidance puts no hard ceiling on it. The honest summary is that it is slow by default and fast when tuned.

### What are WooCommerce cart fragments and do they slow my site?

Cart fragments are a script that uses WordPress admin-ajax to refresh the mini-cart without reloading the page. Before WooCommerce 7.8 in June 2023 the script was enqueued on every page; since then it loads by default only where the cart widget is rendered. Where it does run it is an uncached PHP request, so it adds origin work that grows with traffic.

### How many concurrent users can WooCommerce handle?

There is no single number, because a store has two ceilings. The cached catalog serves thousands of visitors cheaply, while the uncacheable cart and checkout path is capped by your PHP worker pool and database. The honest answer comes from load testing the full journey on a staging copy of your own stack.

### Can I load test my WooCommerce store?

Yes, and because WooCommerce is self-hosted you can test the whole journey including checkout, unlike a hosted platform. Test a staging copy that mirrors production with payments in sandbox, not the live site. Check your host's terms first, since many managed hosts restrict load testing; Kinsta, for example, prohibits it on its platform.

### Do too many plugins slow down WooCommerce?

They can. Each active plugin can add database queries, hook callbacks, and front-end scripts that run on every request, so a store assembled from a dozen or more pays that cost repeatedly. This is the category cost, not a verdict on any single plugin. Measure which ones add the most weight rather than guessing.

### What is wp\_options autoload and why does it matter for WooCommerce?

WordPress loads every option flagged to autoload from the wp\_options table on each page request. As settings and expired transients accumulate, that payload grows and taxes every page. Without a persistent object cache, the server reads those options from the database on every page view, so trimming the table and clearing stale transients helps.

### Does an HTTP load test measure WooCommerce Core Web Vitals?

No. In HTTP mode a protocol load test reads response bytes without running a browser, so there is no Largest Contentful Paint, Interaction to Next Paint, or Cumulative Layout Shift to record. Measuring Core Web Vitals under load needs a real browser that executes the theme and plugin JavaScript the way a shopper's device does.

Related reading

## More from the blog

[![Sale traffic splits at the full page cache: catalog pages are served from cache, while cart and checkout bypass it and land every click's full work on PHP and MySQL.](https://www.evaluat.com/blog/magento-checkout-slow-under-load-cover.svg)](https://www.evaluat.com/blog/magento-checkout-slow-under-load)

### [Why Magento checkout dies first under load](https://www.evaluat.com/blog/magento-checkout-slow-under-load)

[Every sale, the same picture: the homepage is fast, product pages are fine, and checkout is timing out. That is not bad luck. Magento's cache serves the catalog, but cart and checkout hit PHP and MySQL on every click, exactly where the work is heaviest and hardest to share. Here is why, and how to test for it.](https://www.evaluat.com/blog/magento-checkout-slow-under-load)

[Ahmad Farzan · 12 June 2026](https://www.evaluat.com/blog/magento-checkout-slow-under-load)

[![On a Shopify store, the platform sends a cached page fast, but each shopper's browser still has to run the theme, app, and third-party JavaScript, which pushes Core Web Vitals from good toward poor under load.](https://www.evaluat.com/blog/shopify-store-slow-under-load-cover.svg)](https://www.evaluat.com/blog/shopify-store-slow-under-load)

### [Why your Shopify store slows down under load](https://www.evaluat.com/blog/shopify-store-slow-under-load)

[Shopify is hosted, so buyers assume it scales, and its cached storefront genuinely does. The slowdown lives somewhere else: the theme, apps, and third-party JavaScript a merchant adds run in every shopper's own browser, where an HTTP load test never looks. Under load, that is where a Shopify store actually slows. Here is why, and how to see it.](https://www.evaluat.com/blog/shopify-store-slow-under-load)

[Ahmad Farzan · 27 June 2026](https://www.evaluat.com/blog/shopify-store-slow-under-load)

[![Three load-testing models compared: HTTP-script sends requests with no browser, shared-browser puts many virtual users in one browser, and real-browser gives each virtual user its own isolated browser. Only the real-browser model captures what users actually see.](https://www.evaluat.com/blog/real-browser-load-testing-cover.svg)](https://www.evaluat.com/blog/real-browser-load-testing)

### [Real-browser load testing, explained](https://www.evaluat.com/blog/real-browser-load-testing)

[Most load testing tools fire HTTP requests at your server. A few share one browser across many simulated users. Real-browser load testing gives every virtual user its own isolated browser, so it measures what your customers' browsers actually do under load. Here is how the three models differ, what each one can and cannot see, and when each is the right call.](https://www.evaluat.com/blog/real-browser-load-testing)

[Ahmad Farzan · 5 May 2026](https://www.evaluat.com/blog/real-browser-load-testing)

See it on your site

Test in real browsers.\
Debug in real sessions.

## Want to see this measured on your app?

30 minutes. We build a scenario on your real customer journey, run a small test, and walk you through the report.

[Book a demo](https://www.evaluat.com/demo?intent=performance) [How it works](https://www.evaluat.com/how-it-works)
