How to prepare WooCommerce for a big sale: performance checklist
This is a pre-sale checklist for engineers running WooCommerce stores before a high-traffic event: Black Friday, a product launch, a big campaign. The work splits into three phases, two weeks before, the final days, and the sale itself, and every step here is concrete enough to execute from a terminal.
Two weeks before: update everything, on staging only
The worst moment to discover a plugin conflict is at peak traffic. Clone production into a staging copy, run every update there, and only then promote the same versions to production. After that, freeze: no version changes until the sale ends.
# on the staging copy wp core update wp core update-db wp plugin update --all wp theme update --all wp wc update
After updating, walk the money path by hand: add a product to the cart, enter a coupon code, pay through your gateway in test mode, and confirm the order email arrives. If a payment plugin was updated, test every payment method you offer, not just cards.
Verify that backups actually restore
A backup you have never restored is an assumption, not a backup. Take the newest full backup, files plus database, restore it into a scratch environment and boot the site from it.
# in the scratch environment wp db import nightly_dump.sql wp db check wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_type='shop_order';"
With High-Performance Order Storage (HPOS) enabled, count wp_wc_orders instead. The number should match production. Plan one extra snapshot for the hour before the sale starts, so the rollback point is minutes old rather than a day old.
Load-test the checkout, not the homepage
A cached homepage survives almost anything, so that number proves nothing. Cart and checkout bypass the page cache and hit PHP and the database on every request. Script the real flow, add to cart, open cart, load checkout, and run it against staging with a tool like k6:
k6 run --vus 50 --duration 5m checkout-flow.js
While it runs, watch the server rather than the test output:
mysqladmin extended-status | grep -E "Threads_(connected|running)" tail -f /var/log/mysql/slow.log grep max_children /var/log/php8.2-fpm.log
Find the point where response times start climbing and aim to hold 2-3x your expected peak. The usual fixes, in order of payoff: raise pm.max_children as far as RAM allows, enable a persistent object cache, and index or rewrite whatever the slow query log keeps showing.
The final days: images, plugins, cache rules
Run an image pass over the catalogue and any new banners: resize to the dimensions the theme actually renders, compress, and serve WebP where possible.
cwebp -q 82 banner.png -o banner.webp
Then remove weight you do not need during the sale:
- Disable heatmaps, session recorders and secondary marketing pixels for the week. Each one adds front-end payload and extra admin-ajax or REST traffic. Keep one lightweight analytics tool.
- Confirm your page cache excludes cart, checkout and account pages, and bypasses whenever a WooCommerce session cookie is present.
# nginx fastcgi-cache example
if ($request_uri ~* "/(cart|checkout|my-account)") { set $skip_cache 1; }
if ($http_cookie ~* "woocommerce_items_in_cart|wp_woocommerce_session") { set $skip_cache 1; }
Verify from outside: curl -I https://shop.example/checkout/ should show a BYPASS or MISS cache header on every request. A cached checkout page mixes customers' carts and breaks nonces, which is far worse than being slow.
Session and database tuning basics
First, cron. WooCommerce cleans its wp_woocommerce_sessions table and runs scheduled actions through WP-Cron, which fires on page views and behaves badly under load. Disable it with define('DISABLE_WP_CRON', true); in wp-config.php and run it from the system instead:
*/5 * * * * wp --path=/var/www/shop cron event run --due-now
Second, the options table. Delete expired transients and check how much data autoloads on every request; under about 1 MB is healthy:
wp transient delete --expired wp db query "SELECT SUM(LENGTH(option_value))/1024/1024 AS autoload_mb FROM wp_options WHERE autoload='yes';"
Third, MySQL itself. Set innodb_buffer_pool_size large enough to hold your working set, align max_connections with the number of PHP-FPM workers, and leave the slow query log on through the sale so you can see what hurts.
During the sale: monitor, do not deploy
Freeze everything. No deploys, no plugin toggles out of curiosity, no settings experiments. The only changes allowed are the ones on your pre-written degradation plan: which plugins get switched off first if load climbs, and what the fallback is if the payment gateway degrades.
Watch four numbers: PHP-FPM active processes against pm.max_children, MySQL Threads_running, disk space, because logs and sessions grow quickly, and orders per minute. A sudden drop in order rate with normal traffic usually means checkout is broken, and the alert should come from monitoring before a customer emails you. If checkout errors spike and you cannot isolate the cause quickly, restore the snapshot you verified: that is exactly why it exists.
The checklist
- Clone production to staging, 14 days out.
- Update core, plugins, themes and the WooCommerce database on staging, then test a full purchase.
- Promote the same versions to production and freeze all updates.
- Restore the latest backup into a scratch environment and verify order counts.
- Load-test the scripted checkout flow until you can hold 2-3x the expected peak.
- Resize and compress images, serve WebP.
- Disable heatmaps, session recorders and secondary marketing pixels.
- Verify cache exclusions for cart, checkout and account pages with curl.
- Confirm a persistent object cache is active.
- Delete expired transients and check the autoloaded options size.
- Move WP-Cron to a system cron entry.
- Review innodb_buffer_pool_size, max_connections and the slow query log.
- Take a fresh snapshot one hour before the sale starts.
- During the sale: watch FPM, MySQL, disk and order rate, and deploy nothing.
If you would rather not run this stack yourself, our WordPress hosting in our own Riga data centre already includes server-side caching, a Redis object cache and daily backups, which takes a good part of this list off your plate before you start.