
Introduction
WordPress powers a huge portion of the web, from marketing sites and blogs to high-traffic publishers, membership platforms, and WooCommerce stores. That flexibility is a major strength, but it also means WordPress performance can vary dramatically depending on your theme, plugins, hosting stack, caching strategy, and database health.
That’s why WordPress load testing is essential. A site that feels fast with a handful of users can slow down or fail completely during a product launch, seasonal campaign, viral traffic spike, or checkout rush. With proper load testing, performance testing, and stress testing, you can identify bottlenecks before real visitors do.
In this guide, you’ll learn how to load test WordPress sites with LoadForge using realistic Locust scripts. We’ll cover basic page traffic, logged-in behavior, WordPress REST API usage, and WooCommerce-style purchase flows. Along the way, we’ll look at how to spot slow plugins, overloaded PHP workers, uncached pages, and database-heavy operations. Because LoadForge runs distributed load tests from cloud-based infrastructure with real-time reporting, global test locations, and CI/CD integration, it’s a practical way to validate WordPress performance under real-world traffic conditions.
Prerequisites
Before you begin load testing WordPress with LoadForge, make sure you have:
- A WordPress site you’re allowed to test
- A staging or pre-production environment that closely matches production
- Admin or developer access to identify plugins, themes, caching, and critical user flows
- A list of important URLs and actions to test, such as:
- Home page
- Category archives
- Blog posts
- Search
- Contact forms
- Login
- WooCommerce product pages
- Cart and checkout
- WordPress REST API endpoints
- Test accounts for authenticated scenarios
- If using WooCommerce:
- Test products
- Test coupons
- A payment gateway in sandbox mode
- LoadForge account access
You should also know whether your WordPress environment includes:
- Full-page caching such as Varnish, Cloudflare, or a WordPress caching plugin
- Object caching like Redis or Memcached
- CDN delivery for static assets
- PHP-FPM or Apache worker limits
- MySQL or MariaDB performance constraints
- Security plugins or WAF rules that may rate-limit test traffic
A key best practice for WordPress performance testing is to test both cached and uncached experiences. Home pages may look excellent under load because they’re cached, while logged-in pages, search, cart, checkout, and admin-ajax requests can still collapse under concurrency.
Understanding WordPress Under Load
WordPress is dynamic by default. Each uncached request can involve:
- PHP execution
- Plugin hooks and filters
- Theme rendering
- Database queries
- External API calls
- Session or cookie handling
- Object cache lookups
Under load, common WordPress bottlenecks include:
Slow Plugins
Some plugins add expensive database queries, remote API calls, or heavy processing to every request. Builders, analytics plugins, related-post systems, and poorly optimized WooCommerce extensions are common culprits.
Uncached Dynamic Pages
Pages for logged-in users, carts, checkouts, account dashboards, and personalized content often bypass page cache. These requests hit PHP and MySQL directly and are much more expensive than anonymous cached page views.
Database Saturation
WordPress stores posts, metadata, options, users, comments, and WooCommerce data in MySQL. Under concurrency, expensive meta queries, autoloaded options, and order creation can overwhelm the database.
admin-ajax.php Overuse
Many themes and plugins rely on /wp-admin/admin-ajax.php for live search, cart fragments, filtering, and background actions. This endpoint often becomes a hidden hotspot during load testing.
Search and Filter Operations
Native WordPress search and WooCommerce layered navigation can become expensive as content and product counts grow.
Authentication and Session Overhead
Login flows, nonce validation, authenticated REST API access, and WooCommerce sessions all add processing overhead that cached anonymous traffic won’t reveal.
A realistic WordPress stress testing strategy should therefore include a mix of:
- Anonymous cached browsing
- Uncached dynamic page requests
- Logged-in user behavior
- Search and filter activity
- REST API traffic
- E-commerce transactions if applicable
Writing Your First Load Test
Let’s start with a basic WordPress load test that simulates anonymous visitors browsing a typical content site. This is useful for baseline performance testing and validating your caching layer.
Basic WordPress browsing test
from locust import HttpUser, task, between
class WordPressAnonymousUser(HttpUser):
wait_time = between(1, 4)
@task(5)
def homepage(self):
self.client.get("/", name="GET /")
@task(3)
def category_page(self):
self.client.get("/category/news/", name="GET /category/news/")
@task(4)
def blog_post(self):
self.client.get("/2024/11/holiday-launch-prep/", name="GET /blog-post")
@task(2)
def author_page(self):
self.client.get("/author/editorial-team/", name="GET /author-page")
@task(1)
def search(self):
self.client.get("/?s=wordpress+hosting", name="GET /search")What this test does
This script models anonymous traffic to a standard WordPress site:
- The home page gets the most traffic
- Category and blog post pages represent normal browsing
- Search adds a more expensive dynamic request
- Different task weights create a more realistic traffic mix
Why this matters for WordPress
A simple test like this helps answer questions such as:
- Is page caching working correctly?
- Are category and post pages equally fast?
- Does search degrade response times?
- Are there slow theme templates or plugin hooks on specific pages?
If your cached pages are still slow under moderate load, the issue may be outside WordPress itself, such as CDN misconfiguration, poor origin performance, or limited PHP workers serving cache misses.
When you run this in LoadForge, you can scale the number of virtual users across distributed cloud infrastructure and inspect real-time reporting to compare median, p95, and error rates across these endpoints.
Advanced Load Testing Scenarios
Basic browsing tests are useful, but they rarely expose the most important WordPress bottlenecks. The following scenarios are more realistic and more valuable for serious performance testing.
Scenario 1: Logged-in WordPress users and REST API traffic
Many WordPress sites have members, editors, LMS students, subscribers, or customers. Logged-in users bypass full-page caching, making them much more expensive to serve.
The script below logs in through wp-login.php, stores the session cookies, then performs authenticated actions including a REST API request.
from locust import HttpUser, task, between
import re
class WordPressLoggedInUser(HttpUser):
wait_time = between(2, 5)
username = "loadtest_user"
password = "SuperSecurePassword123!"
def on_start(self):
login_page = self.client.get("/wp-login.php", name="GET /wp-login.php")
match = re.search(r'name="redirect_to" value="([^"]*)"', login_page.text)
redirect_to = match.group(1) if match else "/wp-admin/"
payload = {
"log": self.username,
"pwd": self.password,
"rememberme": "forever",
"wp-submit": "Log In",
"redirect_to": redirect_to,
"testcookie": "1"
}
with self.client.post(
"/wp-login.php",
data=payload,
name="POST /wp-login.php",
allow_redirects=True,
catch_response=True
) as response:
if "wp-admin-bar-my-account" in response.text or "/wp-admin/profile.php" in response.text:
response.success()
else:
response.failure("Login failed")
@task(3)
def dashboard_profile(self):
self.client.get("/wp-admin/profile.php", name="GET /wp-admin/profile.php")
@task(2)
def account_page(self):
self.client.get("/my-account/", name="GET /my-account/")
@task(2)
def authenticated_rest_api(self):
self.client.get(
"/wp-json/wp/v2/users/me",
name="GET /wp-json/wp/v2/users/me"
)
@task(1)
def ajax_request(self):
self.client.post(
"/wp-admin/admin-ajax.php",
data={
"action": "heartbeat"
},
name="POST /wp-admin/admin-ajax.php"
)What this test reveals
This test is useful for membership sites, editorial workflows, LMS platforms, and customer account areas. It helps uncover:
- Slow login handling
- Session and cookie overhead
- Expensive uncached account pages
- Slow REST API responses for authenticated users
admin-ajax.phpsaturation
If response times spike sharply when switching from anonymous to logged-in traffic, your WordPress site likely depends heavily on page caching and lacks enough backend capacity for dynamic requests.
Scenario 2: WooCommerce product browsing, add-to-cart, and cart updates
WooCommerce is one of the most common WordPress performance pain points. Product pages may be partly cacheable, but cart and checkout interactions are dynamic and often trigger session writes, AJAX requests, and database activity.
This test simulates a realistic WooCommerce shopper.
from locust import HttpUser, task, between
import random
import re
class WooCommerceShopper(HttpUser):
wait_time = between(2, 6)
product_ids = [128, 245, 366]
product_urls = [
"/product/classic-logo-hoodie/",
"/product/premium-beanie/",
"/product/limited-edition-sticker-pack/"
]
@task(4)
def browse_shop(self):
self.client.get("/shop/", name="GET /shop/")
self.client.get("/product-category/apparel/", name="GET /product-category/apparel/")
@task(5)
def view_product(self):
product_url = random.choice(self.product_urls)
self.client.get(product_url, name="GET /product-page")
@task(2)
def add_to_cart(self):
product_id = random.choice(self.product_ids)
self.client.post(
"/?wc-ajax=add_to_cart",
data={
"product_id": str(product_id),
"quantity": "1"
},
headers={
"X-Requested-With": "XMLHttpRequest"
},
name="POST /?wc-ajax=add_to_cart"
)
@task(2)
def view_cart(self):
self.client.get("/cart/", name="GET /cart/")
@task(1)
def update_cart(self):
cart_page = self.client.get("/cart/", name="GET /cart/ (prefetch)")
nonce_match = re.search(r'name="woocommerce-cart-nonce" value="([^"]+)"', cart_page.text)
if nonce_match:
nonce = nonce_match.group(1)
self.client.post(
"/cart/",
data={
"coupon_code": "",
"apply_coupon": "",
"update_cart": "Update cart",
"woocommerce-cart-nonce": nonce,
"_wp_http_referer": "/cart/"
},
name="POST /cart/ update"
)Why this scenario matters
This script is closer to real e-commerce load testing than simple page hits. It helps you evaluate:
- Product page rendering performance
- WooCommerce AJAX endpoint scalability
- Session and cart storage behavior
- Cart page response times under concurrency
- Whether plugins such as search, recommendations, shipping calculators, or analytics slow down the purchase path
For WooCommerce performance testing, pay special attention to p95 and p99 response times, not just averages. Averages can look acceptable while a subset of users experiences very slow cart behavior.
Scenario 3: Checkout flow with guest customer details
Checkout is one of the most important WordPress stress testing scenarios for any WooCommerce store. It is dynamic, database-heavy, and usually triggers taxes, shipping, coupon validation, payment initialization, and order creation.
The script below simulates a guest checkout using realistic billing details. This should only be run in a staging environment with sandbox payment settings.
from locust import HttpUser, task, between
import random
import re
class WooCommerceCheckoutUser(HttpUser):
wait_time = between(3, 7)
product_ids = [128, 245]
def on_start(self):
product_id = random.choice(self.product_ids)
self.client.post(
"/?wc-ajax=add_to_cart",
data={
"product_id": str(product_id),
"quantity": "1"
},
headers={
"X-Requested-With": "XMLHttpRequest"
},
name="POST add_to_cart (setup)"
)
@task
def checkout(self):
checkout_page = self.client.get("/checkout/", name="GET /checkout/")
nonce_match = re.search(r'name="woocommerce-process-checkout-nonce" value="([^"]+)"', checkout_page.text)
if not nonce_match:
return
nonce = nonce_match.group(1)
payload = {
"billing_first_name": "Test",
"billing_last_name": "User",
"billing_company": "LoadForge QA",
"billing_country": "US",
"billing_address_1": "123 Performance Ave",
"billing_address_2": "Suite 400",
"billing_city": "Austin",
"billing_state": "TX",
"billing_postcode": "78701",
"billing_phone": "5125550199",
"billing_email": f"testuser{random.randint(1000,9999)}@example.com",
"shipping_method[0]": "flat_rate:1",
"payment_method": "cod",
"terms": "on",
"woocommerce-process-checkout-nonce": nonce,
"_wp_http_referer": "/?wc-ajax=update_order_review"
}
self.client.post(
"/?wc-ajax=checkout",
data=payload,
headers={
"X-Requested-With": "XMLHttpRequest"
},
name="POST /?wc-ajax=checkout"
)What this scenario helps you find
Checkout testing exposes some of the most business-critical performance issues:
- Slow order creation
- Database write contention
- Expensive shipping or tax calculations
- Coupon and discount logic bottlenecks
- Payment plugin overhead
- Session locking issues
- Third-party API latency
If your store handles flash sales, promotions, or holiday traffic, this is the scenario that matters most. A WordPress site can survive heavy browsing traffic and still fail at the exact moment users try to pay.
Analyzing Your Results
Once your WordPress load test is running in LoadForge, focus on more than just “requests per second.” For meaningful performance testing, you should review several metrics together.
Response time percentiles
Look at:
- Median response time for typical experience
- p95 for degraded but common slow requests
- p99 for worst-case user experience
For WordPress, p95 is especially important on:
- Search pages
- Login
- My Account
admin-ajax.php- Cart
- Checkout
- REST API endpoints
A homepage with a 300 ms median may look healthy, while checkout at p95 of 8 seconds indicates serious risk.
Error rates
Watch for:
- 5xx errors from PHP or upstream services
- 429 rate limiting from security plugins or WAFs
- 403 responses from bot protection
- 502/504 gateway timeouts from overloaded PHP-FPM or reverse proxies
WordPress-specific failures often appear as intermittent AJAX or REST API errors before full site outage happens.
Throughput and concurrency behavior
As you increase user count, ask:
- Does throughput scale linearly?
- Do response times rise gradually or suddenly?
- Is there a clear saturation point?
A sudden jump in latency usually means a backend limit has been reached, such as database connections, PHP workers, or CPU.
Compare cached vs dynamic endpoints
Segment your results between:
- Cached anonymous pages
- Logged-in pages
- WooCommerce dynamic endpoints
- REST API calls
- Search and AJAX traffic
This is where LoadForge’s real-time reporting is especially useful. You can quickly see whether the bottleneck is broad or isolated to a specific WordPress feature.
Correlate with backend monitoring
During the test, also monitor:
- CPU and memory
- PHP-FPM worker usage
- MySQL slow queries
- Redis or object cache hit rates
- Web server connections
- CDN cache hit ratio
If / stays fast but /cart/ slows dramatically while MySQL CPU spikes, the problem is likely database-bound WooCommerce logic rather than general hosting capacity.
Performance Optimization Tips
After load testing WordPress, these are some of the most common improvements teams make:
Cache aggressively where safe
Use full-page caching for anonymous traffic and ensure category, post, and landing pages are cacheable. Exclude only truly dynamic pages.
Add object caching
Redis or Memcached can significantly improve WordPress and WooCommerce performance by reducing repetitive database queries.
Audit plugins
Disable or replace plugins that add slow queries, remote API calls, or heavy frontend/AJAX behavior. Load testing often reveals that one plugin is responsible for most degradation.
Optimize WooCommerce paths
Focus on:
- Cart fragments
- Search/filter plugins
- Shipping calculations
- Payment gateway plugins
- Product variation lookups
These often dominate e-commerce performance testing results.
Reduce autoloaded options
Large autoload option payloads can slow every uncached request. Audit and trim them.
Tune PHP workers and database connections
If response times jump sharply under concurrency, you may simply need more PHP-FPM workers, better process tuning, or a stronger database tier.
Use a CDN for static assets
Images, CSS, JavaScript, and fonts should be offloaded from the origin wherever possible.
Optimize search
Native WordPress search often performs poorly at scale. Consider dedicated search solutions if search becomes a bottleneck in your load tests.
Common Pitfalls to Avoid
WordPress load testing is easy to do badly if the test doesn’t reflect real traffic. Avoid these common mistakes:
Testing only the homepage
A cached homepage tells you very little about actual WordPress performance. Always include dynamic and uncached flows.
Ignoring logged-in users
Membership sites, LMS platforms, communities, and WooCommerce stores often serve their most important traffic uncached.
Forgetting AJAX and REST API endpoints
Many WordPress bottlenecks live in:
/wp-admin/admin-ajax.php/wp-json/.../?wc-ajax=...
These endpoints are often more important than the visible page itself.
Running checkout tests in production
Checkout and order creation tests should be done in staging with sandbox payments unless you have a carefully controlled production-safe approach.
Not handling nonces and sessions correctly
WordPress and WooCommerce use nonces for many form submissions. If your script skips them, the test may generate unrealistic failures.
Letting security tools block the test
WAFs, bot protection, rate limiting, and login protection plugins can skew results. Coordinate with your infrastructure team so LoadForge traffic is allowed during testing.
Using unrealistic wait times
Real users don’t request pages back-to-back with zero delay. Add realistic think time with between() to avoid distorted results.
Failing to test from multiple regions
If your audience is geographically distributed, run tests from multiple global test locations. LoadForge makes this straightforward and helps you see whether CDN and origin performance vary by region.
Conclusion
WordPress load testing is about much more than checking whether your homepage stays online. To truly prepare for traffic spikes, you need to test the full stack: cached browsing, logged-in sessions, REST API traffic, AJAX behavior, and WooCommerce purchase flows. That’s how you uncover slow plugins, overloaded PHP workers, database contention, and fragile checkout paths before they affect real users.
With LoadForge, you can build realistic Locust-based WordPress performance testing scripts, run distributed load testing from cloud-based infrastructure, analyze results in real time, and integrate testing into your CI/CD pipeline. Whether you’re running a content site, membership platform, or WooCommerce store, LoadForge gives you the visibility you need to stress test WordPress with confidence.
If you’re ready to find bottlenecks and harden your site before the next traffic surge, try LoadForge and start load testing your WordPress environment today.
LoadForge Team
LoadForge is a load and performance testing platform built on Locust. Our team has been shipping load tests against production systems since 2018, and we write these guides from real customer engagements.
Related guides
Keep going with more guides from the same category.

BigCommerce Load Testing: How to Performance Test BigCommerce Stores
See how to load test BigCommerce stores with LoadForge to validate storefront speed, API performance, and checkout stability.

Drupal Load Testing: How to Stress Test Drupal Sites with LoadForge
Find out how to load test Drupal sites with LoadForge to detect performance issues and maintain speed during heavy traffic.

Headless CMS Load Testing: How to Performance Test Content APIs with LoadForge
Learn how to load test headless CMS platforms and content APIs with LoadForge to ensure fast, scalable content delivery.