WooCommerce Speed Optimisation: Real Store Benchmark Results

How I took a WooCommerce store from PageSpeed 42 to 91 — the exact steps, plugins, and settings. Real data from a Bulgarian WooCommerce store.

Dobromir Dechev
Dobromir WordPress agency owner

WooCommerce is slow by default

The average WooCommerce store scores 45-65 on PageSpeed Insights before optimisation. After auditing dozens of e-commerce sites, the pattern is consistent: slow hosting, unoptimised images, too many plugins, and a page builder that was never meant to be fast.

This article is a case study of one complete optimisation project - a Bulgarian WooCommerce store - with the exact steps taken, the tools used, and the before/after numbers.


The store: before optimisation

Setup at project start:

  • Hosting: SiteGround Go Geek (Sofia datacenter)
  • Theme: Flatsome (popular WooCommerce theme)
  • Page builder: WPBakery on shop and category pages
  • Products: ~340 products with multiple variations
  • Images: all JPEG, no WebP, uploaded at original camera size (3-6MB each)
  • Plugins: 31 active plugins

Baseline measurements:

MetricMobileDesktop
PageSpeed Insights3458
TTFB1,240ms1,240ms
LCP5.8s3.2s
TBT2,100ms890ms
CLS0.210.08

Cart abandonment rate from analytics: 76%

These numbers are not unusual for a 3-year-old WooCommerce store that has grown organically without performance work.


The WooCommerce caching problem

Before any optimisation, it is worth understanding why WooCommerce is harder to cache than a regular WordPress site.

The cart cookie problem:

When a user adds a product to their cart, WooCommerce sets a session cookie: woocommerce_items_in_cart. Most full-page caching layers are configured to bypass the cache when this cookie is present - because serving a cached page to a user with items in their cart could show stale cart contents.

The result: every shopper who has ever added something to their cart gets uncached PHP responses for every page. TTFB goes from 30ms (cached) to 800-1200ms (uncached) for the majority of your active users.

The correct fix is not to disable caching. It is to configure fragment caching (ESI) for the cart widget, so the main page body is still served from cache while only the cart fragment is loaded dynamically via AJAX.

WP Rocket handles this automatically. LiteSpeed Cache uses Edge Side Includes (ESI) on LiteSpeed servers. Cloudways Breeze handles it via AJAX. Verify your caching plugin has this correctly configured before anything else.

WooCommerce scripts on every page:

WooCommerce loads its full JavaScript bundle (cart, checkout, product gallery) on every page of your site, not just shop pages. On a homepage with no products, WooCommerce's scripts still load.

The fix for this is to conditionally dequeue WooCommerce scripts on non-WooCommerce pages:

add_action( 'wp_enqueue_scripts', function() {
    if ( ! is_woocommerce() && ! is_cart() && ! is_checkout() ) {
        wp_dequeue_style( 'woocommerce-general' );
        wp_dequeue_style( 'woocommerce-layout' );
        wp_dequeue_style( 'woocommerce-smallscreen' );
        wp_dequeue_script( 'woocommerce' );
        wp_dequeue_script( 'wc-cart-fragments' );
    }
}, 99 );

This alone reduced Total Blocking Time by 340ms on the client's homepage.


Phase 1: Hosting migration

Decision: SiteGround Go Geek to Cloudways Vultr High Frequency 2GB

The TTFB on SiteGround was 1,200-1,400ms. No amount of plugin optimisation will overcome a 1.2 second server response time. This was the non-negotiable first step.

Migration process:

  1. Clone the site to local using Local by Flywheel
  2. Create new Cloudways server (Vultr HF 2GB, Frankfurt)
  3. Deploy from local to Cloudways
  4. Test thoroughly on the Cloudways temporary URL
  5. Switch DNS with 30-minute TTL to minimise downtime

Post-migration TTFB: 195ms

That single change, before touching any plugins or images, improved mobile PageSpeed from 34 to 52. Server response time is not one factor among many - it is the foundation everything else builds on.


Phase 2: Caching setup

Plugin: WP Rocket

WP Rocket was installed and configured with these specific settings for WooCommerce:

  • Page cache: enabled

  • Cache for logged-in users: disabled (WooCommerce customers should not see cached account pages)

  • Separate cache file for mobile: enabled (Flatsome serves different markup on mobile)

  • Delay JavaScript execution: enabled, with these scripts excluded from delay:

    • jquery.min.js (many WooCommerce plugins depend on jQuery being available immediately)
    • wc-cart-fragments.min.js (cart AJAX)
    • Any script causing console errors when delayed
  • Remove unused CSS: enabled

  • Preload cache: enabled (sitemap-based crawler)

  • CDN: configured after Phase 3

Cache warm-up after configuration: WP Rocket crawled all 340 product pages and all category pages. Full cache warm-up took 12 minutes.

After caching setup:

MetricMobileDesktop
PageSpeed Insights6782
TTFB195ms195ms
LCP3.1s1.8s
TBT890ms320ms

Progress, but not finished. LCP is still above the 2.5s threshold.


Phase 3: Image optimisation

The problem: 340 products with 2-8 images each. Most images were uploaded at full camera resolution (3000-5000px wide, 2-6MB each). WordPress generates thumbnails but served JPEG at whatever size was requested.

The fix:

  1. Install Imagify (premium, €9/mo) to bulk-convert and compress all existing images
  2. Enable WebP generation and serving (via <picture> element with JPEG fallback)
  3. Set WordPress maximum image size to 1920px via wp-config.php
  4. Configure WooCommerce product image sizes to match actual display dimensions
// Add to functions.php
add_image_size( 'product-large', 800, 800, true );
add_image_size( 'product-thumb', 300, 300, true );

// Stop WordPress generating 12 different sizes
add_filter( 'intermediate_image_sizes_advanced', function( $sizes ) {
    unset( $sizes['medium_large'] );
    unset( $sizes['1536x1536'] );
    unset( $sizes['2048x2048'] );
    return $sizes;
});

After bulk processing with Imagify:

  • Average product image: 2.1MB JPEG → 38KB WebP (94% size reduction)
  • All images served via WebP with JPEG fallback
  • Total image payload on category page: 4.2MB → 280KB

After image optimisation:

MetricMobileDesktop
PageSpeed Insights7991
LCP2.1s1.1s

LCP is now passing (under 2.5s). The improvement came entirely from reducing image payload and serving WebP.


Phase 4: CDN setup

CDN: BunnyCDN (Pull Zone)

BunnyCDN was configured as a pull CDN for all static assets. The origin is the Cloudways server; BunnyCDN caches images, CSS, JS, and fonts at edge nodes in Frankfurt, London, Amsterdam, and Warsaw.

WP Rocket's CDN feature rewrites all static asset URLs to the BunnyCDN hostname (cdn.storename.com via a CNAME record).

Cost: approximately €1-2/month for this store's traffic volume (90-120GB/month of asset delivery).

After CDN:

  • LCP for EU visitors outside Frankfurt: improved by 200-400ms
  • Cache hit rate after 24 hours: 94%

Phase 5: JavaScript audit

After the above changes, the remaining score bottleneck was Total Blocking Time - caused by third-party JavaScript.

Active third-party scripts at the time:

  • Google Analytics 4 (via gtag.js)
  • Facebook Pixel + Facebook Conversions API
  • Hotjar
  • LiveChat widget
  • Google Tag Manager (loading all of the above)

Changes made:

  1. Moved all analytics loading into Google Tag Manager with a 3-second delay trigger (GTM fires 3 seconds after page load)
  2. Replaced Hotjar with Microsoft Clarity (lighter, free)
  3. Configured LiveChat to load on user interaction only (first scroll or click)
  4. Removed Facebook Pixel from non-product pages (product and checkout pages only)

WP Rocket's "Delay JavaScript execution" delayed GTM until after first user interaction, which eliminated GTM-related TBT entirely.

After JavaScript audit:

MetricMobileDesktop
PageSpeed Insights9197
TTFB195ms195ms
LCP1.8s0.9s
TBT180ms95ms
CLS0.040.02

Phase 6: WPBakery removal

The shop and category pages still used WPBakery for layout. WPBakery loads its full CSS and JS on every page, including pages where it adds no layout value.

We rebuilt the shop template and category archive using a custom child theme with GeneratePress as the parent. Product grid layout was handled with native WooCommerce hooks and custom CSS rather than a page builder.

WPBakery removed from shop pages:

  • Removed ~180KB CSS
  • Removed ~220KB JavaScript
  • Lighthouse TBT on category page: 180ms → 45ms

This was the most impactful change after the hosting migration, but also the most time-intensive. Rebuilding the category template took approximately 8 hours.


Final results

After all phases:

MetricBeforeAfterChange
PageSpeed Mobile3493+59
PageSpeed Desktop5899+41
TTFB1,240ms195ms-84%
LCP (mobile)5.8s1.6s-72%
TBT (mobile)2,100ms45ms-98%
CLS0.210.03-86%

Business metrics (3-month comparison):

Cart abandonment rate: 76% → 61% (-15 percentage points)

Revenue attribution: this is impossible to isolate cleanly (the store also ran a promotional period), but the client reported a 23% increase in completed orders in the three months post-optimisation versus the same period in the prior year.


The order of operations

If you take one thing from this case study, it is that order matters:

  1. Fix hosting first. Nothing else works if TTFB is above 600ms.
  2. Configure caching correctly. This includes WooCommerce cart exclusions and mobile cache.
  3. Optimise images. WebP conversion and correct sizing have the highest impact-to-effort ratio.
  4. Add a CDN. BunnyCDN at €1-2/month is the cheapest performance improvement available.
  5. Audit JavaScript. Third-party scripts are the most common cause of failing Core Web Vitals.
  6. Remove the page builder from performance-critical pages. Save this for last - it requires the most development time but delivers measurable LCP and TBT improvements.

WooCommerce performance: what the data says

The relationship between page speed and conversion rate in e-commerce is well-documented. Google's research across multiple large retail datasets consistently finds:

  • 100ms improvement in load time → 1% increase in conversion rate
  • 1-second improvement → 7% improvement in conversions
  • 3-second load time causes 53% of mobile users to abandon

Applied to the case study store:

  • LCP improved from 5.8s to 1.6s: a 4.2-second improvement
  • Theoretical conversion rate impact: 4.2 × 1-2% = 4-8% conversion rate increase
  • Actual measured improvement: 13 percentage point reduction in abandonment rate

The actual improvement exceeded the theoretical prediction, likely because multiple factors compound. Faster WooCommerce checkout steps reduce decision fatigue, faster product image loading reduces second-guessing, and a faster overall experience increases trust.


The tools used in this optimisation

For transparency, here is the exact tool stack used across all phases:

Hosting migration:

  • Local by Flywheel: local development environment for staging the migration
  • Cloudways: managed cloud hosting platform (Vultr HF 2GB, Frankfurt)
  • Breeze: Cloudways caching plugin for Varnish integration

Performance optimisation:

  • WP Rocket: full-page caching, JavaScript delay, preloading
  • BunnyCDN: content delivery network for static assets
  • Imagify: bulk image conversion to WebP and compression

Theme/template:

  • GeneratePress: lightweight parent theme replacing Flatsome
  • Custom child theme: shop and category page templates

Analytics and monitoring:

  • PageSpeed Insights API: automated score monitoring
  • Chrome DevTools: waterfall analysis, performance profiling
  • WP Umbrella: ongoing uptime and performance monitoring post-migration

Total cost of the optimisation tools (ongoing):

  • Cloudways Vultr HF 2GB: €25/month (hosting 3 client sites on this server, so effective cost ~€8/month for this store)
  • WP Rocket: €59/year
  • BunnyCDN: approximately €1.50/month at this store's traffic volume
  • Imagify: one-time bulk processing credit of approximately €15 for 1,400 images

Ongoing monthly cost: approximately €12/month. One-time migration cost: approximately €800 in development time (12 hours at €67/hour).


WooCommerce performance benchmarks by store size

The optimisation techniques above scale differently depending on store size. Here is how to prioritise by store complexity:

Small store (under 50 products, under 100 orders/month):

Priority order:

  1. Hosting (move to Cloudways if TTFB is above 500ms)
  2. Image optimisation (highest ROI for least development time)
  3. Caching plugin configuration
  4. CDN

At this scale, removing page builders is optional - the performance gain from other optimisations may be sufficient.

Medium store (50-500 products, 100-1,000 orders/month):

Priority order:

  1. Hosting (Cloudways Vultr HF or Kinsta)
  2. Redis object caching (significantly reduces uncached checkout TTFB)
  3. Image optimisation + CDN
  4. Caching with correct WooCommerce exclusions
  5. WooCommerce script removal from non-shop pages
  6. JavaScript audit (GTM, pixels, chat)

At this scale, checkout performance has direct revenue impact. Redis object caching on the hosting stack is worth the add-on cost.

Large store (500+ products, 1,000+ orders/month):

Priority order:

  1. Dedicated managed hosting (Kinsta WooCommerce plan or equivalent)
  2. Database optimisation (WooCommerce sessions cleanup, indexing on large tables)
  3. Redis with persistent connections
  4. Remove page builder from shop/category pages entirely
  5. Consider server-side rendering for product listings (headless WooCommerce)
  6. Database query profiling (Query Monitor in staging, EXPLAIN queries on slow pages)

At this scale, uncached response time on product category pages (which cannot be fully cached due to dynamic sorting and filtering) is the main bottleneck. Database optimisation has disproportionate impact.


Measuring ROI of WooCommerce performance work

Clients often ask whether the investment in performance optimisation is worth it. Here is a framework for calculating ROI:

Data needed:

  • Current monthly revenue
  • Current average order value
  • Current cart abandonment rate
  • Current checkout page TTFB

Expected improvement from optimisation:

  • TTFB improvement: measure after hosting migration
  • Abandonment rate improvement: estimate 5-15 percentage points (conservative: 5pp, aggressive: 15pp)

Revenue calculation:

Example: Store doing €8,000/month with 70% abandonment rate and €80 AOV.

Current completed orders per month: (Visitors × add-to-cart rate) × (1 - 0.70) If 5,000 visitors/month with 10% add-to-cart: 500 cart sessions × 30% completion = 150 orders/month × €80 = €12,000 (this does not match - the example is simplified)

Simpler approach: if abandonment drops from 70% to 60% (10pp improvement):

  • Current completed sessions: 30% of 500 = 150 sessions
  • After optimisation: 40% of 500 = 200 sessions
  • Additional sessions: 50 × €80 AOV = €4,000/month additional revenue
  • Annualised: €48,000/year

Against an optimisation investment of €1,000-2,000 in development time plus €100-200/year in additional hosting and tool costs, the ROI is clear.

This calculation is the most compelling argument for clients who question the cost of performance work. Present it with conservative numbers - a 5 percentage point abandonment reduction rather than 10 - and it still shows strong ROI for any store doing meaningful revenue.


Was this article helpful?