Skip to main content
System.Return('/syslog')
Web DevelopmentPerformanceWordPress

WooCommerce Optimization: Caching & Core Web Vitals

How I optimized a heavy WooCommerce store on a DigitalOcean droplet to pass Google's Core Web Vitals — cutting TTFB by 75% and improving mobile conversion rates within 30 days.

In 2020, I launched Mushtariyat.com with one goal: get a working e-commerce store online as fast as possible. WooCommerce on a DigitalOcean droplet. Feature delivery first, optimization later.

By 2023, the site was handling 5,000+ monthly visits and the optimization debt was due. Mobile conversion rates were suffering because Google’s Core Web Vitals were failing — specifically TTFB (Time to First Byte) and LCP (Largest Contentful Paint). I had a budget of exactly zero additional dollars and needed to fix everything through architectural changes and open-source tools.

Here’s exactly what I did and why each change worked.


The Problem: WooCommerce’s Hidden Performance Tax

WooCommerce is resource-intensive by design. Every page load involves multiple database queries — loading products, categories, cart state, session data. On a standard LAMP stack, this creates a compounding bottleneck:

  • Database: Every page load queries WooCommerce tables for products, variations, session data, and transients
  • PHP: Each query requires PHP-FPM to execute, parse results, and render the page
  • Disk: WordPress core + WooCommerce + 15 plugins = thousands of PHP files to parse on every request

On a standard droplet with 2GB RAM and a spinning hard drive, this chain could take 1.2 seconds before the first byte of HTML was sent. On mobile connections common in the UAE and Oman, that translated to LCP times of 4.5+ seconds.

Google’s threshold for “Good” LCP is 2.5 seconds. We were failing by nearly double.


Fix 1: Replacing Apache with NGINX + PHP-FPM

The droplet was running Apache with mod_php. Apache’s process-based model means every concurrent connection spawns a new PHP process. Under load, this consumes RAM aggressively and leaves processes idle waiting for disk I/O.

The migration: LEMP stack (Linux, NGINX, MySQL, PHP-FPM).

NGINX’s event-driven architecture handles concurrent connections with a fraction of the RAM overhead. PHP-FPM’s process manager (PM) allows fine-tuning the number of PHP workers based on available memory.

# NGINX config for WordPress - key performance directives
fastcgi_cache_path /var/run/nginx-cache levels=1:2 
 keys_zone=WORDPRESS:100m inactive=60m max_size=1g;

fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_valid 200 60m;
fastcgi_skip_cache on $args;

The result: idle RAM usage dropped 30%. The server could now handle the same traffic with headroom for spikes.


Fix 2: Redis Object Caching

The biggest bottleneck in WordPress isn’t PHP — it’s the database. Without object caching, WordPress queries the database for every single option, transient, and session record on every page load.

For WooCommerce, this is especially expensive. The plugin stores cart data, session state, product visibility flags, and API credentials as transients — database records that are queried hundreds of times per page render.

The fix: Redis as an object cache backend.

  1. Installed Redis server on the Linux droplet: apt install redis-server
  2. Configured the Predis PHP library
  3. Installed the redis-cache WordPress plugin with the object-cache.php drop-in

The mechanics: instead of querying MySQL for a transient, WordPress checks Redis first. Redis holds the data in RAM. A RAM lookup is orders of magnitude faster than a MySQL query against disk.

Result: Database queries per page dropped from ~90 to ~15. TTFB dropped from 1,200ms to 300ms.


Fix 3: Disabling WooCommerce Cart Fragments

This is the most underappreciated performance issue in WooCommerce.

By default, WooCommerce fires an AJAX request (/?wc-ajax=get_refreshed_fragments) on every single page load to check whether the cart has changed. This request bypasses all page caching — it has to hit the PHP stack every time because the response is dynamically generated from session data.

On high-traffic pages or during campaigns, this creates a storm of uncacheable requests that hammers the server.

The fix: Dequeue the cart fragments script on pages where it doesn’t matter.

add_action('wp_enqueue_scripts', 'dequeue_wc_cart_fragments', 11);
function dequeue_wc_cart_fragments() {
    // Keep cart fragments only on store and product pages
    // Remove from blog posts, landing pages, about, etc.
    if (!is_shop() && !is_product() && !is_cart() && !is_checkout()) {
        wp_dequeue_script('wc-cart-fragments');
        wp_dequeue_style('woocommerce-smallscreen');
    }
}

This is a surgical change: the cart still works on product and checkout pages. On blog posts and landing pages — which make up 60% of most WooCommerce stores’ traffic — it eliminates an unnecessary backend round-trip.


Fix 4: Image Optimization via Cloudflare CDN

High-resolution cosmetic product photography is beautiful and heavy. A hero image at 3MB is common for beauty e-commerce. On mobile, this directly causes failed LCP scores.

The fix: Three layers of optimization.

Layer 1 — CDN offload: All /wp-content/uploads offloaded to Cloudflare. Images are served from Cloudflare’s edge network instead of the origin droplet. The first byte of an image arrives from a server geographically close to the user, not from a DigitalOcean server in Frankfurt.

Layer 2 — WebP conversion at the edge: Cloudflare’s Polish feature auto-converts images to WebP format in transit. No WordPress plugin changes required. A 3MB JPEG becomes a 400KB WebP before it reaches the browser.

Layer 3 — Fetch priority: Added fetchpriority="high" to the LCP image (usually the hero). This tells the browser to deprioritize other resources and load this image as early as possible.

<!-- Before -->
<img src="hero.jpg" alt="WooCommerce store hero banner showing product categories">

<!-- After -->
<img src="hero.jpg" alt="WooCommerce store hero banner showing product categories" fetchpriority="high">

For the hero image specifically, this cut LCP by 0.8 seconds on mobile.


The Results

MetricBeforeAfterChange
TTFB1,200ms300ms-75%
LCP (Mobile)4.5s1.8s-60%
DB queries/page~90~15-83%
CPU under load100%60%-40%

All Core Web Vitals moved from “Failing” to “Good.” Google Search Console showed green across all metrics within two weeks of deployment.

Business impact: Mobile conversion rate increased by 1.2% within 30 days. On an e-commerce store doing $50,000/month in revenue, that’s $600/month in additional revenue from a zero-dollar architectural change.


The Takeaway

Performance optimization for e-commerce follows a predictable pattern:

  1. Measure first. Use Google PageSpeed Insights and Search Console. Know which metrics are failing and by how much before changing anything.
  2. Cache everything that doesn’t change per-request. Redis handles database queries. NGINX fastcgi_cache handles generated pages. CDN handles static assets.
  3. Remove things that don’t need to exist. Cart fragments on non-commerce pages. Multiple plugins doing the same job. Large images without WebP conversion.
  4. Offload to the edge. The origin server should do one thing: generate dynamic content. Everything else belongs at a CDN.

The budget was zero. The improvement was 75% faster TTFB and passing Core Web Vitals. This work paid for itself within the first month through improved conversion rates — a practical example of the cost optimization philosophy I’ve applied across multiple projects.