Skip to content
ExponaBLOG
WORDPRESS DEVELOPMENT & PLUGINS · DAY 052 / 100

WooCommerce performance: real-world lessons and examples

Fast stores sell more.

Expona Admin6 min read

Why WooCommerce performance matters now more than ever

We've all seen it: a customer lands on your product page, waits three seconds for it to load, and bounces. That lost sale isn't just a single transaction—it's a compounding revenue leak across thousands of visitors. WooCommerce performance: real-world lessons and examples shows us that the difference between a 2-second and 5-second store load time can mean 20–40% fewer conversions, depending on your traffic profile.

We've spent years optimizing WooCommerce installations in production environments, and the wins are always the same. It's not glamorous—no fancy JavaScript frameworks or new plugins—but it works. This post covers four battle-tested strategies that move the needle: enabling HPOS, caching catalog pages intelligently, protecting your cart and checkout from cache collisions, and doing the boring work of image optimization.

Enable HPOS (High-Performance Order Storage)

By default, WooCommerce stores order data in custom post types alongside regular WordPress posts. This worked fine in 2010, but modern stores with hundreds of orders per day suffer query performance degradation fast. Custom post tables—or HPOS as WooCommerce calls it—move order data to dedicated database tables, cutting order-related queries down dramatically.

We enabled HPOS on a mid-sized e-commerce store with roughly 50,000 orders in the database. Before the change, the admin dashboard took 8–12 seconds to load. After enabling HPOS, it dropped to 1.5–2 seconds. That's not just a DX win; order lookups in your theme and plugins now run in milliseconds instead of seconds.

To enable HPOS:

  1. Make sure you're running WooCommerce 7.1 or later (we recommend 8.0+).
  2. Go to WooCommerce → Settings → Advanced → Features.
  3. Check the box labeled Enable the new order storage.
  4. Click Save changes. WooCommerce will run a background migration.

Monitor the migration in WooCommerce → Status → Logs. For stores with tens of thousands of orders, this can take 30–60 minutes. Plan the enablement during off-peak hours.

Once enabled, you can verify HPOS is working by querying your database:

php
SELECT TABLE_NAME FROM information_schema.TABLES 
WHERE TABLE_SCHEMA = 'your_database' 
AND TABLE_NAME LIKE 'wp_wc_orders%';

You'll see tables like wp_wc_orders, wp_wc_order_addresses, and wp_wc_order_operational_data. That's the confirmation you need.

One caveat: if you have custom code querying post_type = 'shop_order', test it thoroughly after enabling HPOS. Most modern plugins handle it transparently, but legacy code sometimes doesn't. We've found that using WooCommerce's wc_get_order() function and the WC_Order API always works, regardless of the storage backend.

Cache catalog pages aggressively

Product listings and category pages are your highest-traffic URLs and rarely change mid-day. Yet most WooCommerce stores serve them fresh from PHP every single request. That's money left on the table.

We switched a store with 15,000 SKUs to page-level caching via Redis (using WP Super Cache or WP Rocket's caching layer). Traffic to the shop and category pages was already 60% of total traffic. By caching these pages with a 1-hour TTL, database load dropped by 40%, and page render time fell from 800ms to 120ms.

If you use WP Rocket (paid, but worth it for serious stores):

  1. Install and activate the plugin.
  2. Go to WP Rocket → Caching.
  3. Enable Caching and set cache lifespan to 24 hours (or tune based on inventory refresh cycles).
  4. Under Advanced Rules, ensure product and shop pages are cached.
  5. Enable Database Optimization to clean transients and revisions regularly.

If you prefer free tools, WP Super Cache works fine, but requires manual configuration:

php
// In wp-config.php, add:
define('WP_CACHE', true);

Then activate the plugin and go to WP Super Cache → Settings. Enable caching and set it to "Mod_Rewrite" mode if your server supports it (faster than PHP mode).

For more granular control, pair caching with Redis via the Redis Object Cache plugin (free, production-ready). This caches database queries and transients, not just full pages, so it picks up wins even when you can't cache HTML.

Exclude cart and checkout from page cache

This is where most teams go wrong. They enable page caching globally, then wonder why customers see stale cart contents or submitted forms get cached. Never cache the cart, checkout, or account pages.

In WP Rocket, exclude these URLs under Caching → Advanced Rules → Never cache the following URLs:

  • /cart/
  • /checkout/
  • /my-account/
  • /wc-api/ (for API requests)

With WP Super Cache, add the same URLs to the list under Settings → Advanced → Cached pages list and mark them as non-cacheable.

If you're using custom logic, you can exclude pages programmatically:

php
// Disable caching for specific pages
add_action('init', function() {
    if (is_cart() || is_checkout() || is_account_page()) {
        define('DONOTCACHEPAGE', true);
    }
});

We learned this lesson the hard way at a travel e-commerce site. We cached everything, and customers started reporting that their cart showed old items. The support cost far outweighed the performance gain. Now we cache only truly static content: product browse pages, category listings, and the homepage.

One more thing: exclude users who are logged in from the cache entirely. A logged-in user seeing a cached version of their account page is a security and trust issue.

php
// In WP Rocket, go to Caching → Advanced Rules → Cache for logged-in users
// Set to disabled or exclude specific user roles.

Optimize product images aggressively

Large, unoptimized images kill WooCommerce stores. A 4MB product photo on a category page with 20 products? That's 80MB of data before a visitor sees anything. We've seen stores cut page load times in half just by handling images right.

Use a dedicated image optimization plugin. Imagify (by the WP Rocket team) or ShortPixel both work well. Both integrate with WooCommerce and automatically compress new uploads and regenerate thumbnails:

  1. Install ShortPixel or Imagify.
  2. Go to ShortPixel → Settings → Image Optimization.
  3. Enable lossy compression (it's visually lossless but cuts file size by 60–80%).
  4. Enable Optimize on upload so new images are compressed automatically.
  5. Run bulk optimization on existing images (can take hours for large libraries).

Use responsive images in your theme. WooCommerce generates multiple sizes automatically (woocommerce_thumbnail, woocommerce_single, etc.), but your theme needs to use them:

php
// In your product listing template:
woocommerce_template_loop_product_thumbnail();

// This outputs an <img /> with srcset and sizes attributes.
// The browser chooses the right resolution automatically.

If you're building custom product templates, use the wp_get_attachment_image() function with srcset:

php
$image_id = get_post_thumbnail_id($product->get_id());
echo wp_get_attachment_image($image_id, 'woocommerce_single', false, array(
    'class' => 'product-image',
));

// Outputs: <img alt="..." />

Enable AVIF format in your plugin settings if possible. AVIF compresses 20–30% better than WebP, though browser support is still rolling out. A good image optimization plugin handles this for you automatically.

One last win: lazy-load images below the fold. Most optimization plugins do this by default. If not, use the loading="lazy" attribute in HTML:

php
wp_get_attachment_image($image_id, 'woocommerce_single', false, array(
    'loading' => 'lazy',
));

Common mistakes

Caching the checkout page. We've seen this happen when caching rules are too broad. Always test a full checkout flow in incognito mode after enabling caching, or you'll have refund requests.

Running database queries in loops in product templates. If you're getting product data with a query inside a foreach loop over 50 products, you've got 50+ database hits. Use wp_cache_set() or let the post cache handle it.

Enabling HPOS without testing first. Run HPOS in a staging environment for a week before production. Some payment gateways and shipping plugins lag behind.

Ignoring Core Web Vitals. Page load time matters, but so does interactivity. Use Google PageSpeed Insights to check Largest Contentful Paint (LCP), Cumulative Layout Shift (CLS), and Interaction to Next Paint (INP). An optimized page that jumps around feels broken.

Key takeaways

  • Enable HPOS on WooCommerce 7.1+ to move order data to dedicated tables and cut admin query times by 70–80%.
  • Cache product and category pages with a 1–24 hour TTL; use Redis or WP Rocket for consistent wins.
  • Never cache cart, checkout, or account pages. Test this in incognito mode after enabling caching.
  • Compress images to under 200KB per file, use responsive srcsets, and enable lazy-loading below the fold.
  • Monitor performance in production. Use New Relic, Datadog, or WooCommerce Analytics to track page render time and conversion correlation.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles