
How to Load Test a WordPress Site Before a Traffic Spike
Testing your cached homepage tells you nothing. Here's how to load test the paths that actually break, and why to tell your host first.

Most WordPress load tests measure the wrong thing and produce a reassuring number that means nothing.
The typical approach points a testing tool at the homepage and ramps up virtual users until something breaks. The homepage is cached, so the tool is measuring how fast your cache can return a file. It will handle enormous load, because serving a static file is easy. Then the sale starts, real shoppers hit checkout, and the site falls over at a fraction of the traffic your test said it could handle.
WordPress VIP's own load testing documentation puts the underlying problem plainly: "The most critical aspects of site resilience and performance are cache hit rate and the speed of page generation for origin requests (i.e. uncached requests)." Those are the two things a homepage test doesn't measure.
This walks through doing it properly, and starts with the step that matters most legally and practically: telling your host.
TL;DR
- Notify your host before you start. Policies vary enormously: WordPress VIP requires a support ticket outlining objectives and methodology, Kinsta prohibits load testing on its platform entirely, and AWS requires a form submitted two weeks ahead for simulated DDoS events.
- Test uncacheable paths, not the cached homepage. WordPress VIP notes that on a well-cached site "on average only 1/10th of the user requests would reach the origin datacenter."
- Use k6 (JavaScript-based, CI-friendly) or Apache JMeter (GUI-based, Java). Both are HTTP-level tools that don't run JavaScript or render pages, so neither measures front-end performance.
- The four test types are documented distinctly: load (expected traffic), stress (peak traffic), spike (sudden massive rush), and soak (average load sustained for hours or days).
- Read p95 and p99, not averages, plus error rate. An average hides exactly the tail your visitors notice.
- There is no official WooCommerce or WordPress.org guide to pre-sale load testing. WooCommerce's own Black Friday checklist doesn't mention it at all.
Prerequisites
Before you begin:
- Written confirmation from your host that load testing is permitted, and on what terms. This is not optional. See Step 1.
- A staging environment that mirrors production as closely as possible. MagicWP's one-click staging creates a clone from the same dashboard.
- A recent backup before any test that writes data. Checkout tests create orders. MagicWP's on-demand backups cover this.
- k6 installed locally, or JMeter. Full documentation for k6 is at Grafana's k6 docs; JMeter's is at jmeter.apache.org.
- Knowledge of your current traffic, so your test targets are grounded in something. Your analytics peak concurrent users is the starting point.
- A list of your uncacheable paths. For WooCommerce that's cart, checkout, and My Account; for a membership site it's whatever sits behind login.
Step 1 - Get Permission From Your Host
Load testing generates traffic patterns that look identical to a denial-of-service attack, because functionally that's what they are. Automated defences don't distinguish intent.
Policies genuinely differ, and the range is wider than people expect:
- WordPress VIP requires it: "Prior to initiating any load or stress testing, notify VIP by creating a Support ticket and outline the objectives and planned methodology of the load test."
- Kinsta prohibits it outright: "Load testing is prohibited on the MyKinsta platform," directing users to speed-testing tools like Pingdom and GTmetrix instead.
- AWS distinguishes standard load testing from simulated DoS events, requiring a Simulated Events form submitted "at least two (2) weeks in advance of the start date" for the latter.
So there is no universal rule, and assuming one gets sites blocked or accounts suspended. Ask your host, in writing, before you generate a single virtual user. Include what you plan to test, from where, at what volume, and when.
Important: This applies to staging as well as production on most platforms, since staging usually shares infrastructure. Don't assume staging is exempt.
Step 2 - Decide What You're Actually Testing
Four documented test types, each answering a different question:
| Type | What it does | Question it answers |
|---|---|---|
| Load test | Simulates expected user activity | Can we handle our anticipated traffic? |
| Stress test | Applies load at peak traffic levels | What happens at our busiest realistic moment? |
| Spike test | Sudden, massive rush of utilisation | Do we survive a viral post or a sale opening? |
| Soak test | Average load sustained for hours or days | Do we leak memory or degrade over time? |
For a sale event, you want a spike test: the traffic pattern is a sudden concentration, not a gradual ramp.
For general capacity planning, start with a load test at your expected peak and a stress test somewhat above it.
Soak tests catch a different class of bug entirely — memory leaks, connection pool exhaustion, log files filling disks — that a short test never reveals.
Step 3 - Model a Realistic User Journey
This is the step that separates a useful test from a meaningless one.
Do not test only the homepage. WordPress VIP's documentation explains why in one line: "URLs that have been saved to the edge cache will have a much faster load time for future requests (hits)," with the consequence that on a well-cached site "on average only 1/10th of the user requests would reach the origin datacenter."
Hammering a cached URL measures your cache. Your cache is fine. It's the other tenth that will break.
A realistic WooCommerce journey:
- Land on the homepage (cached)
- Browse a category page (cached)
- View a product page (cached)
- Add to cart (uncached, writes a session)
- View cart (uncached)
- Proceed to checkout (uncached, creates a draft order)
- Place order (uncached, writes to the database, calls a payment gateway)
Steps 4 through 7 are where the load actually lands. A test that stops at step 3 tells you your CDN works.
For a membership or forum site: log in, view a members-only page, post a comment. All uncacheable.
For a publisher expecting a viral post: the article itself is cacheable, so the honest test is whether your cache handles the concurrency and what your cache-miss rate looks like at that volume.
WordPress VIP adds a methodological caution worth heeding: don't "intentionally use cache-busting query parameters, non-GET requests, or requests with cookies that might invalidate caching at the edge" unless that's specifically what you're testing. In other words, be deliberate about which requests you're forcing to origin, rather than accidentally making everything a miss and drawing false conclusions.
Step 4 - Write the Test
k6 uses JavaScript. A test that ramps to 20 virtual users, holds, and ramps down:
import { check } from 'k6';
import http from 'k6/http';
export const options = {
thresholds: {
http_req_failed: ['rate<0.01'], // under 1% errors
http_req_duration: ['p(99)<1000'], // 99% under 1 second
},
scenarios: {
average_load: {
executor: 'ramping-vus',
stages: [
{ duration: '10s', target: 20 },
{ duration: '50s', target: 20 },
{ duration: '5s', target: 0 },
],
},
},
};
export default function () {
const res = http.get('https://staging.example.com/shop/');
check(res, { 'response code was 200': (res) => res.status == 200 });
}The pieces:
scenariosdefines the load pattern.ramping-vuswithstagesgives you ramp-up, hold, ramp-down.stagesare duration-and-target pairs describing the virtual user curve.thresholdsare pass/fail criteria. k6's documentation defines them as "the pass/fail criteria that you define for your test metrics. If the performance of the system under test (SUT) does not meet the conditions of your threshold, the test finishes with a failed status."checkvalidates individual responses without failing the test.
Threshold syntax is <metric>: ['<aggregation> <operator> <value>'], so p(95)<200 means 95% of requests under 200 milliseconds.
To model the full journey, extend the default function with the sequence of requests, carrying cookies between them so the session persists, and adding realistic pauses between steps so you're not simulating users who click instantly.
Existing WordPress scripts. The cachewerk/k6 repository on GitHub contains k6 scripts for WordPress and WooCommerce, including a woo-checkout.js and a general WordPress script that crawls sitemaps. Worth knowing what it is before you rely on it: it's maintained by CacheWerk, the company behind commercial object-cache products, and its README frames it primarily as benchmarking object-cache backends rather than as a general-purpose load test suite. Read the scripts before adapting them.
JMeter as an alternative is, per its own documentation, "a 100% pure Java application designed to load test functional behavior and measure performance," supporting HTTP/HTTPS, REST, FTP, JDBC, LDAP, JMS and more, with a GUI test IDE that can record from a browser plus a headless CLI mode.
The practical choice: JMeter if you want a GUI and browser recording, k6 if you want tests as code that live in version control and run in CI. Both are HTTP-level tools that do not execute JavaScript or render pages like a browser, so neither measures front-end performance. They measure your server.
Step 5 - Run It, Starting Small
Never open with your target load. Ramp deliberately:
- Smoke test first. One or two virtual users, briefly, confirming the script works and hits the right URLs. Most script bugs surface here for free.
- Run at a low level, maybe 10% of target, and confirm the numbers look sane.
- Step up gradually, watching server metrics alongside k6's output.
- Reach target, hold, and observe.
- Push past target if you want to find the breaking point, which is often the most useful part.
Watch your server while the test runs, not just the test output. PHP-FPM worker usage, database connections, CPU, and memory tell you why something broke. The load test only tells you that it did.
The specific log line worth grepping for during a WordPress test is PHP-FPM's server reached pm.max_children setting (N), consider raising it. That's definitive evidence you ran out of PHP workers rather than hitting a slow query.
Step 6 - Read the Results Correctly
k6 reports a set of built-in metrics. The ones that matter:
| Metric | What it is |
|---|---|
http_req_duration |
Total request time, equal to sending plus waiting plus receiving |
http_req_waiting |
Time waiting for the response, which k6 documents as "time to first byte" |
http_req_failed |
The rate of failed requests |
http_reqs |
Total requests generated |
vus / vus_max |
Active and maximum virtual users |
iterations |
Completed runs of your test function |
Read percentiles, not averages. An average response time of 400 ms sounds fine and can hide a p99 of 8 seconds. That p99 is one visitor in a hundred having a terrible experience, and at scale that's a lot of people. Set thresholds on p95 and p99.
Error rate is the first thing to check. A test where response times look great and 12% of requests returned 502 is a failed test, and it's easy to miss if you only look at duration.
Watch where the curve bends. The useful finding usually isn't a single number, it's the point where response time starts climbing non-linearly. That's your actual capacity, and it's typically well below where errors start.
Compare against a baseline. A single test run tells you how the site performed once. Run the same test before and after a change to learn anything causal.
Step 7 - Act on What You Find
Errors under load with acceptable response times below it points to PHP worker exhaustion. Check for the max_children warning. Options: more workers, more caching to reduce workers needed, or infrastructure that scales.
Response times climbing steadily with load points to a resource ceiling: database connections, CPU, or memory. Profile an uncached request with Query Monitor to find the expensive part.
A specific endpoint far slower than others is a code problem, not a capacity problem. Checkout being slow while product pages are fast means the checkout path has a bottleneck worth profiling individually.
Everything holding up fine means either you're genuinely ready, or you tested cached URLs. Verify your cache hit rate during the test before celebrating.
Cache hit rate lower than expected is often the real finding. If 40% of requests reached origin when you expected 10%, your caching rules need work, and that's a bigger win than adding capacity.
Infrastructure that scales automatically, as MagicWP's managed hosting does, changes the shape of the worker-exhaustion problem by adding capacity when uncacheable load rises. It doesn't fix a slow query on the checkout path, which is why profiling the individual slow endpoint still matters.
A Gap Worth Naming
There is no official WooCommerce or WordPress.org guide to load testing before a high-traffic sale.
WooCommerce's own Black Friday checklist recommends speed testing tools, a CDN, image optimisation, and considering a server upgrade. It doesn't mention load testing, k6, JMeter, or capacity testing of any kind.
The closest official guidance is WooCommerce's scaling documentation, which suggests monitoring average add-to-cart calls per minute as an indicator of server demand. That's a good production monitoring signal, and it's a different thing from testing capacity beforehand.
So if you're looking for the authoritative WordPress source on this, it doesn't exist. What does exist is WordPress VIP's load testing documentation, which is the most useful platform-level guidance available and is where most of the methodology in this article comes from.
Frequently Asked Questions
Will load testing get me banned by my host? It can. Policies vary widely: WordPress VIP requires advance notification via support ticket, Kinsta prohibits load testing on its platform entirely, and AWS requires a form two weeks ahead for simulated DoS events. Always ask your host in writing first, including for staging.
Why is testing my homepage not enough? Because it's cached. WordPress VIP notes that on a well-cached site only about a tenth of requests reach the origin. Testing a cached URL measures your CDN, not your application. Test cart, checkout, login, and other uncacheable paths.
Should I load test production or staging? Staging, for anything that writes data or risks downtime. WordPress VIP's own guidance is nuanced, recommending baselines before launch and again once real traffic is arriving, so there are legitimate production scenarios, but they need host approval and careful scoping.
k6 or JMeter? k6 for tests as code, version control, and CI integration. JMeter for a GUI and browser-based test recording. Both are HTTP-level and neither renders pages or runs JavaScript, so neither measures front-end performance.
How many virtual users should I test with? Start from your analytics peak concurrent users, then test at that level, above it, and well above it to find where the curve bends. Virtual users aren't equivalent to real concurrent visitors, since a virtual user with no think time generates far more requests than a human.
What's a good response time under load? It depends on the path, but set thresholds on percentiles rather than averages. A common starting point is p95 under one second and an error rate below 1%. The more useful finding is where response time starts climbing non-linearly.
Does load testing measure Core Web Vitals? No. k6 and JMeter operate at the HTTP level and don't render pages or execute JavaScript. They measure server capacity. Core Web Vitals need field data from real browsers.
Does WooCommerce publish official load testing guidance? No. Its Black Friday checklist doesn't mention load testing at all. Its scaling documentation recommends monitoring add-to-cart calls per minute in production, which is monitoring rather than pre-event testing.
Conclusion
A load test is only as good as the paths it exercises. Test your cached homepage and you'll get a number that makes everyone feel good and predicts nothing. Test add-to-cart, checkout, and login, and you'll find out what actually happens when the sale opens.
The sequence: ask your host first, build a realistic journey through your uncacheable paths, ramp up gradually while watching server metrics alongside test output, and read p95 and p99 with error rate rather than averages. When something breaks, the server-side metrics tell you why: worker exhaustion looks different from a slow query, and the fixes are different too.
Then fix and re-run. A load test you run once before a sale tells you where you stand. A load test you run before and after each change tells you whether you're improving.
Next steps
- Read Grafana's k6 documentation for the full scripting API, and its thresholds guide for pass/fail criteria.
- Review the k6 metrics reference so you're reading the right numbers.
- Compare test types in Grafana's types of load testing overview.
- Read WordPress VIP's load testing documentation, the most useful platform-level guidance publicly available.
- Consider Apache JMeter if you prefer a GUI-driven workflow.
- Check MagicWP's performance documentation for the caching and scaling behaviour your test will be measuring.
Get the best of MagicWP in your inbox.
Monthly engineering notes, product updates, and WordPress performance tips. No spam, unsubscribe anytime.

