10 practices that keep WooCommerce fast at scale

Binadit Tech Team 7 August 2026 8 min czytaj
10 practices that keep WooCommerce fast at scale

Who this is for

This checklist is for engineers running WooCommerce stores that have outgrown basic shared hosting: stores doing consistent daily order volume, running flash sales, or syncing inventory with an ERP. WooCommerce is a WordPress plugin built on PHP and MySQL, so most of its scaling problems are the same problems you would solve for any managed infrastructure for SaaS workload: database contention, cache invalidation, and background job processing. If you already run a store with more than a few hundred SKUs or a few thousand orders a month, these practices apply directly to you.

None of this requires a full replatform. Most of it is configuration, query discipline, and infrastructure choices you can make incrementally.

10 practices for WooCommerce performance at scale

1. Move sessions and cart data off MySQL

WooCommerce stores cart and session data in the wp_options and wp_woocommerce_sessions tables by default. Under concurrent traffic, this creates write contention on tables that are already read-heavy for product and pricing data.

Moving sessions to Redis removes that write pressure entirely. It also makes session lookups sub-millisecond instead of a MySQL round trip on every cart update.

// wp-config.php or a custom session handler plugin
define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);
define('WP_REDIS_TIMEOUT', 1);
define('WP_REDIS_DATABASE', 2);

2. Separate object cache from page cache

Object caching (via Redis or Memcached) caches individual database query results: a product lookup, a term query, a user meta fetch. Page caching serves entire rendered HTML pages. These solve different problems and need different invalidation rules.

WooCommerce category and cart pages are not fully cacheable at the page level because they're user-specific or price-sensitive, but they benefit enormously from object caching underneath. Running both, correctly separated, is what actually gets you both speed and correctness. We cover the specific Redis parameters that matter for WooCommerce in how to configure Redis for a high-traffic WooCommerce store.

3. Exclude cart, checkout, and account pages from full-page cache

Full-page caching plugins that don't understand WooCommerce's dynamic fragments will happily cache a cart page with someone else's items in it. This is a correctness bug disguised as a performance win.

# Nginx example: never cache dynamic WooCommerce endpoints
location ~* ^/(cart|checkout|my-account) {
    proxy_no_cache 1;
    proxy_cache_bypass 1;
}

Everything else, product pages, category pages, the homepage, can be cached aggressively at the edge or with Nginx FastCGI cache, as long as cart fragments load via AJAX after the page renders.

4. Offload cart fragments to AJAX, not full page reloads

WooCommerce's default "mini cart" updates via an AJAX call to wc-ajax=get_refreshed_fragments. This is good, it's what allows the rest of the page to be cached, but it's frequently misconfigured to run on every page load even when the cart hasn't changed.

Throttle or conditionally disable fragment refreshes on pages where the cart state can't have changed (blog posts, static pages). This alone can cut PHP-FPM load by a noticeable percentage on content-heavy stores.

5. Index your custom query patterns, not just WooCommerce defaults

WooCommerce's default indexes on wp_postmeta and wp_wc_order_stats handle common queries fine, but custom filters, ERP sync jobs, or reporting plugins often introduce meta queries that aren't indexed at all.

-- Find slow meta queries via slow query log, then check for missing composite indexes
EXPLAIN SELECT post_id FROM wp_postmeta
WHERE meta_key = '_stock_status' AND meta_value = 'instock';

-- If this scans the full table, add:
ALTER TABLE wp_postmeta ADD INDEX idx_meta_key_value (meta_key, meta_value(20));

Run EXPLAIN on your top 10 slowest queries from the MySQL slow query log quarterly. WooCommerce plugin updates change query patterns; indexes don't keep up automatically.

6. Move order processing and emails to a queue

Sending order confirmation emails, syncing to an ERP, or triggering webhooks synchronously inside the checkout request adds latency directly to the customer's checkout experience. If any of those downstream systems are slow, the customer waits for them.

Use Action Scheduler (already bundled with WooCommerce) or a dedicated queue like Redis-backed jobs to push non-critical post-order tasks out of the request cycle.

// Hook into order completion, but defer the actual work
add_action('woocommerce_order_status_completed', function($order_id) {
    as_schedule_single_action(time(), 'sync_order_to_erp', ['order_id' => $order_id]);
});

This is the same pattern that matters for any high availability infrastructure setup: keep the request path short, push everything else to background workers.

7. Use a CDN for product images and static assets, not just HTML

Product catalogs with hundreds of high-resolution images are the single biggest bandwidth and origin-load contributor on most WooCommerce stores. Serving these through a CDN with a long cache lifetime (30+ days) removes that load from your web servers entirely.

# Cache-Control for WooCommerce media
location ~* .(jpg|jpeg|png|webp|avif)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

Serve WebP or AVIF where the browser supports it. Image weight reduction has a direct, measurable effect on mobile checkout conversion, which matters more for revenue than most backend optimizations.

8. Autoscale PHP-FPM workers based on real concurrency, not fixed pools

A fixed pm.max_children value tuned for average traffic will queue requests during a flash sale and sit idle the rest of the month. Size your pool based on available memory per worker and monitor queue depth, not just CPU.

; php-fpm pool config
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500

pm.max_requests matters more than people think: it forces worker recycling, which prevents slow memory growth from long-running plugin code from degrading performance over a day of uptime.

9. Run read replicas for reporting and sync jobs

Analytics dashboards, ERP sync scripts, and admin reports run expensive aggregate queries against order and product tables. Running these against your primary database competes directly with checkout traffic for the same connection pool and locks.

Pointing reporting and sync workloads at a read replica isolates that load completely. This is a standard pattern across managed infrastructure for SaaS environments, and WooCommerce stores benefit from it just as much as any other transactional application.

10. Load test checkout specifically, not just the homepage

Homepage load times look great in most load tests because it's the most cacheable page on the site. Checkout is the least cacheable, most database-intensive path, and it's the one that directly determines whether a sale completes.

Run load tests that simulate real checkout flows: add to cart, apply a coupon, enter payment details, submit. We go deeper on this pattern in how to optimize checkout infrastructure to maximize conversion rates, and on campaign-specific load patterns in how WooCommerce stores handle campaign traffic.

Rolling this out in an existing team

You don't need to implement all 10 at once, and you shouldn't try to. A practical rollout order for a team with an existing store in production:

  • Week 1-2: Object cache and session offload (practices 1-2). These are infrastructure changes with no code risk and immediate, measurable latency improvement.
  • Week 3-4: Cache exclusion rules and CDN configuration (practices 3, 7). Low risk, high impact on time-to-first-byte for anonymous traffic.
  • Month 2: Query and index audit (practice 5), queue migration for order processing (practice 6). These require more testing since they touch business logic paths.
  • Month 2-3: PHP-FPM tuning and read replica setup (practices 8-9). Do these once you have real production metrics to size against, not before.
  • Ongoing: Checkout-specific load testing (practice 10) becomes a recurring exercise, ideally before every major sale event, not a one-time setup step.

Track before and after numbers for each change: time-to-first-byte, checkout completion time, and PHP-FPM queue depth during peak hours. Without a baseline, you can't tell whether a change actually helped or whether traffic just happened to be lighter that week.

If your team is migrating to this setup from shared hosting or a generic VPS, the sequencing matters more than the individual steps. Our guide on how to scale WooCommerce infrastructure without downtime covers how to make these changes without a cutover window.

Keeping it fast over time

These 10 practices are not a one-time project. Plugin updates change query patterns, catalogs grow, and traffic mix shifts as marketing campaigns change. Revisit the index audit and load test results quarterly, and treat PHP-FPM and cache configuration as something that gets tuned against real traffic data, not set once and forgotten.

The underlying principle is the same one that applies to any infrastructure management services engagement: keep the request path short, push non-critical work to background jobs, cache aggressively where correctness allows it, and measure before you optimize.

If implementing these yourself is not the best use of your engineering time, our managed services cover all of them by default.