Skip to content

WordPressComplete guide · 10 partsPart 4 of 5

Speed up WooCommerce: the store checklist for cart, checkout, cache and schema

To speed up WooCommerce, cache every page except the cart, checkout and account, stop cart fragments outside the cart, and fix the host, images and plugins that slow the rest.

getReport teamUpdated 26 Sept 202624 min read

To speed up WooCommerce, let a page cache serve every product, category and content page, keep only the cart, checkout and account pages out of it, stop the cart-fragments request on pages that do not need it, and then fix what is left: a slow host, oversized product images and extensions that load on every page. A store is a WordPress site with money on it, so the same checklist also keeps the checkout working and your products looking right in Google. This guide is the map for a whole store, in order of damage, with a link to the detailed guide for each fix. Plan an afternoon, including a test purchase.

This is part 4 of the WordPress speed series, after Elementor performance. If the whole site is slow, not only the shop, start with why is my WordPress site so slow, which covers hosting, caching plugins, page builders and wp-cron for any WordPress site.

Quick answer

  • Measure first. Run the WooCommerce checker on a product page, the home page and a category page.
  • Cache everything except the cart, checkout and my-account, at every layer: plugin, host and CDN. Bypass the cache for shoppers with a WooCommerce cart cookie.
  • Load cart fragments only on the cart and checkout.
  • Check Time to First Byte on an uncached page. Over 800 ms means the server, not the theme, is the bottleneck.
  • Size product images to the layout and trim extensions that load on every page.
  • Keep one source of Product schema with price and stock, and decide what happens to sold-out products.
ProblemWhat shoppers or Google seeFix
Cart or checkout served from a cacheSomeone else's basket, "session expired", failed paymentsExclude cart, checkout and my-account from every cache layer, including the CDN
Cart fragments on every pageSlower pages, a PHP request on every visitLoad wc-cart-fragments only on the cart and checkout
Slow server on uncached pagesA long blank screen before cart and checkoutBetter hosting, current PHP, object cache
Product schema without price or stockNo price, stock or stars in search resultsFill in price and stock, keep one schema source
Out-of-stock product still indexableClicks that bounceKeep with a date and "notify me", or redirect, or noindex

Why is WooCommerce slow?

WooCommerce itself is not heavy. A store is slower than a blog of the same size because of what a shop has to do that a blog does not.

More pages that cannot be cached. A page cache stores one copy of a page and hands it to everyone. That is right for a product page and wrong for a cart, a checkout or an account page, which differ per shopper. Those pages are built by PHP and the database on every request, so they are only as fast as your server. Once a shopper has something in the basket, most setups skip the cache for them on every page.

A request on every page. The cart-fragments script keeps the mini-cart count in the header fresh by asking the server after each page load. That request bypasses the cache.

Heavier pages. Product galleries, zoom and lightbox scripts, variation swatches, reviews, related products, and extensions for filters, wishlists, currency and tracking all add files, often on every page rather than only where they are used.

More work behind the scenes. Orders, stock updates, emails and webhooks run as scheduled jobs, and the admin handles far more data than a blog does.

So a WooCommerce speed optimization has two halves: make the cacheable pages come from the cache, and make the uncacheable ones fast on the server.

How to check a WooCommerce store's speed

The WooCommerce checker first recognises WooCommerce from the page itself: the woocommerce body class, wc-ajax calls, WooCommerce assets or a Product block in the JSON-LD. Then:

  • Cache: it fetches /cart/ and /checkout/ once each, as an anonymous visitor, and reads the response headers. A cache status containing "hit" (x-cache, cf-cache-status, x-litespeed-cache, and the Kinsta, SiteGround, WP Engine, Varnish and nginx equivalents), or an age above 0 together with a public cache-control, counts as served from cache. Nothing is added to a basket and no form is submitted.
  • Cart fragments: it looks for cart-fragments.js in the HTML and for the get_refreshed_fragments request while the page renders in Chromium. On a cart, checkout, basket or my-account address the check passes, because the script belongs there.
  • Product schema: each Product block is checked for offers.price (or offers.lowPrice for variable products), offers.availability, and a rating or review.
  • Stock: an availability of OutOfStock, SoldOut or Discontinued on a page without noindex is flagged.
  • Plugins and caches: the WordPress Doctor's plugin cost and duplicate cache plugin checks run on the same page.

The Cart and checkout table: /cart/ and /checkout/ with status 200, both marked served from cache, and the x-cache: HIT header shown as the evidence for each
Both addresses came back from a cache; the evidence column shows the header that gave it away.

The addresses are fixed: /cart/ and /checkout/. If your cart lives at a translated slug and /cart/ returns 404, the cache finding passes without having seen your real cart; test it by hand as shown under Verify. The schema and stock checks need a product page, and cart fragments are best tested on the home page or a category. For Core Web Vitals and Time to First Byte, run a full getReport report on the same pages.

The checklist, in order of damage

Work in this order: the cache first (it breaks orders), then cart fragments (every page), then the server and page weight, then schema and stock (one product at a time).

1. Take the cart and checkout out of every cache

A cached /cart/ shows the next shopper the basket of whoever loaded it first. A cached /checkout/ carries a security token (a nonce) that was valid for someone else hours ago, so the order form fails with "We were unable to process your order" or a session-expired message, and the shopper leaves. WooCommerce marks these pages as not cacheable, and caching plugins respect that. The problem is usually a layer further out, a host cache or a CDN rule set to "cache everything", that ignores what WordPress says.

Find the cart, checkout and my-account pages under WooCommerce → Settings → Advanced → Page setup; their slugs are what you exclude. Then go layer by layer, because the finding cannot tell which layer answered, only that one did. The evidence header is the clue: x-litespeed-cache points to LiteSpeed, cf-cache-status to Cloudflare, x-kinsta-cache to Kinsta.

  • WP Rocket excludes the standard WooCommerce pages automatically. Renamed ones go under Settings → WP Rocket → Advanced Rules → Never Cache URL(s).
  • LiteSpeed Cache: LiteSpeed Cache → Cache → Excludes → Do Not Cache URIs, and woocommerce_items_in_cart under Do Not Cache Cookies.
  • W3 Total Cache: Performance → Page Cache → "Never cache the following pages".
  • SiteGround: SiteGround Optimizer → Caching → Dynamic Caching → Exclude URLs from Caching.
  • Kinsta skips the standard WooCommerce pages; renamed or translated ones need an exclusion that Kinsta support adds.
  • Cloudflare: Cloudflare's APO for WordPress bypasses the cache for WooCommerce cookies. A custom "cache everything" rule does not. Add a Cache Rule (under Caching or Rules in the dashboard) with Cache eligibility set to "Bypass cache", placed below the rule that caches HTML so it wins:
Text
(http.request.uri.path contains "/cart"
 or http.request.uri.path contains "/checkout"
 or http.request.uri.path contains "/my-account"
 or http.request.uri.path contains "/wp-json/wc/store"
 or http.request.uri.query contains "wc-ajax"
 or http.cookie contains "woocommerce_items_in_cart"
 or http.cookie contains "wp_woocommerce_session_")

The cookie conditions matter as much as the paths: once a shopper has something in the basket, the header cart on every page is personal, so their requests skip the cache everywhere. Purge every cache after the change.

Watch out

A Cloudflare rule with an Edge TTL of "Ignore cache-control header" overrides the no-cache headers WooCommerce sends. If you use one, the bypass rule above is not optional.

Keeping cart, checkout and account pages out of the cache goes through every layer in detail, including the host cache, Varnish, the block checkout and multilingual slugs, and the order to purge in.

2. Stop cart fragments outside the cart

cart-fragments.js keeps the mini-cart count in the header up to date by sending a wc-ajax=get_refreshed_fragments request after each page load. That request cannot be cached, so every visit, including a first visit with an empty cart, costs a PHP process and a round trip. Since WooCommerce 7.8 the script loads only where the classic Mini-Cart widget or block needs it, but many themes and header builders still enqueue it on every page.

WooCommerce itself has no switch for it. Perfmatters has a "Disable Cart Fragmentation" switch, and the report shows a fix recipe for the caching plugin it detects. Otherwise, a must-use plugin dequeues it everywhere except the cart and checkout:

PHP
<?php
// wp-content/mu-plugins/cart-fragments-only-in-cart.php
add_action( 'wp_enqueue_scripts', function () {
    if ( ! function_exists( 'is_cart' ) || is_cart() || is_checkout() ) {
        return;
    }
    wp_dequeue_script( 'wc-cart-fragments' );
}, 100 );

Priority 100 runs after WooCommerce and the theme have enqueued their scripts. Files in wp-content/mu-plugins/ load automatically and survive theme changes.

The cart fragments finding: title with the number of scripts and requests found, why it slows every page, the fix, and the cart-fragments.js URL and the wc-ajax=get_refreshed_fragments request as evidence
The evidence lists the script and the request, so you can confirm both are gone after the change.

There is a trade-off. Without fragments, a cached page can show an old count in the header cart. Adding to the basket from a shop or category page still updates the count there, and once the basket has something in it the cookie bypass from step 1 serves fresh pages. If a stale count elsewhere bothers you, show a plain "Cart" link without a number, or turn on "Redirect to the cart page after successful addition" under WooCommerce → Settings → Products.

WooCommerce cart fragments: stop the AJAX call on every page shows how to measure the request, how to tell whether your theme needs it, and the settings in WP Rocket, LiteSpeed Cache and the common header builders.

3. Make the uncached pages fast: hosting and the server

With the cache set up, visitors browsing products get cached copies, and the server only matters for the pages it has to build: cart, checkout, account, search, filters and every page for a shopper with a full basket. Those are the pages where people pay.

Measure Time to First Byte on the cart or on a search results page, not on the cached home page. Google's web.dev guidance treats 800 ms or less as good. Much more than that on an uncached page means the server is the bottleneck, and no front-end plugin will fix it. Start with what you can change on your current plan: a PHP version WordPress and WooCommerce currently support (8.3 or newer is recommended), and a persistent object cache such as Redis or Memcached if your host offers one.

If that is not enough, the plan itself is the limit. Choosing WooCommerce hosting by measured speed lists what a store needs from a host beyond a blog: PHP workers for uncached requests, an object cache, database room for orders, a real server cron for the Action Scheduler, staging and backups that keep orders, and what hosted payment fields mean for PCI scope. It also shows how to test a copy of your store on candidate hosts and compare them in one table, without relying on reviews.

The general method for comparing any WordPress host is in choosing a WordPress host by measured TTFB, and what a slow server looks like explains the waterfall.

4. Lighten product and category pages

Product and category pages are where shoppers arrive from Google, so their Core Web Vitals count most. The usual weight:

  • Product images. A gallery of full-size uploads is the largest item on most product pages. Let WordPress serve the right size through srcset, set the WooCommerce image widths to what your layout actually shows (for classic themes, Appearance → Customize → WooCommerce → Product Images), and serve WebP or AVIF. See image optimisation.
  • Extensions on every page. Filters, wishlists, swatches, reviews, currency switchers and chat each load files, often on pages that do not use them. Run the WordPress theme & plugin detector on a product page and the cart; what your WordPress plugins cost visitors explains how to trim what it finds.
  • Two caching plugins. They purge at different times, and a cart excluded in one can still be cached by the other. Keep the one your host recommends.
  • The page builder. A builder-made product template adds its own layer; the Elementor guide in this series covers that.

Core Web Vitals for e-commerce product and category pages covers LCP on the main product image, layout shift from price and review widgets, and slow filters in depth.

5. A slow checkout

The checkout is the one page that must not be cached and must not be slow. Beyond hosting, the usual causes are payment gateway scripts loading on every page instead of only at checkout, live shipping-rate lookups that call a carrier's API on each change of address, address-lookup and fraud scripts, and the cart-fragments request competing with the checkout's own calls. Test it the way shoppers use it: add a product, open the checkout, change the country, and time how long the totals take to update.

WooCommerce checkout slow? Find the cause shows how to time the three moments that matter (opening the checkout, updating the totals and Place order) with the browser's Network tab and Query Monitor, and what to change for each cause: the server, gateway scripts and express buttons, carrier and tax lookups, and order emails sent while the shopper waits.

6. A slow admin

A slow dashboard does not hurt shoppers directly, but it slows everyone who runs the store. The admin is never cached, so it shows the server's real speed, and it gets slower as orders, scheduled jobs and extensions pile up. The usual causes are orders still stored in the WordPress posts table instead of High-Performance Order Storage (HPOS, the default for new stores since WooCommerce 8.2), a backed-up Action Scheduler queue because wp-cron does not run often enough, analytics and dashboard widgets, and plugins that call outside services on every admin page.

Speed up the WooCommerce backend shows how to find the slow part with Query Monitor, switch an older store to HPOS safely, clear the scheduled-actions queue with a real server cron, and switch off the admin extras you do not use.

7. Complete the product schema

WooCommerce prints a Product block in JSON-LD on each product page. Google uses its offers.price, offers.availability and rating to show price, stock and stars in search results. When a theme, page builder or SEO plugin replaces that block and drops a field, the rich result goes, and the listing looks like any other link.

Open a product the finding names and check, in the product editor:

  • Price: the regular price is filled in. Without a price, WooCommerce leaves offers out of the schema entirely.
  • Stock: Inventory → Stock status is set. Variable products get an AggregateOffer with a lowPrice, which the check accepts.
  • Reviews: enabled under WooCommerce → Settings → Products → "Enable product reviews". A product with no reviews yet has no rating; leave it until real reviews arrive, because a made-up rating breaks Google's rules for review snippets.

Then make sure only one plugin prints the Product block. View the page source and search for "@type":"Product": it should sit in one <script type="application/ld+json"> block. WooCommerce product schema: price, availability and reviews covers each field, which plugin should own the block, and variable, multi-currency and headless stores. The general rules are in Product schema: price, availability and reviews.

8. Decide what happens to sold-out products

A sold-out product keeps ranking, and every click lands on "Out of stock". Whether you keep the page depends on whether the product comes back.

Coming back: keep the page indexable. Show the expected date, offer a back-in-stock notification, and keep the availability in the schema accurate. Setting the product to "On backorder" when you do take orders changes the availability to BackOrder, which the check does not flag.

Gone for good: redirect the page with a 301 to its replacement or to the category, with your SEO plugin's redirect manager, the Redirection plugin, or the server:

nginx
# nginx, inside the server block
location = /product/blue-kettle-1l/ { return 301 /product-category/kettles/; }

If there is no sensible target, set the product to noindex instead: in Yoast, the product's Yoast SEO box → Advanced → "Allow search engines to show this Product in search results?" → No; in Rank Math, the product's Advanced tab → Robots Meta → No Index. The out-of-stock learn page has the decision in one screen.

The "Hide out of stock items from the catalog" option under WooCommerce → Settings → Products → Inventory removes sold-out products from shop and category listings only. The product page still exists and stays indexable.

9. Security: the checkout is a target

A store holds customer data and takes payments, so it attracts attacks a blog does not: card-testing bots that push hundreds of small payments through the checkout, fraudulent orders with stolen cards, and card-skimming scripts injected into the checkout page to copy what shoppers type. Keep WooCommerce, the payment gateway and every extension updated, give staff the Shop Manager role and two-factor login, and take card data through hosted payment fields so it never touches your server.

WooCommerce security: protect the admin, the checkout and your customers is the full checklist: roles, the "Rate limit Checkout" feature and other defences against card testing, signs of a fraudulent order, REST API keys and webhooks, and customer data in logs, backups and staging.

Skimmers get their own guide because they are hard to see: they hide in the database or in a third-party script and run only on the checkout, for shoppers who are not logged in. Magecart card skimming on WooCommerce shows how to list every script the checkout loads, how a Content Security Policy with reporting catches a new one, and what to do if you find one.

Verify

  • Re-run the checker on the same pages. The findings read "Cart and checkout bypass the page cache", "No cart-fragments request on this page", "WooCommerce Product schema has price, availability and rating" and "Out-of-stock products are handled".
  • Make a test purchase in two browsers at once: add a different product in each, open both carts, and check out one of them. Each should see only its own basket.

From a terminal, request the cart twice and read the headers (use your real slug):

Shell
curl -sI https://example.com/cart/ | grep -iE 'cache|age'
curl -sI https://example.com/cart/ | grep -iE 'cache|age'

The second response should still say no-store or a cache status of BYPASS, MISS or DYNAMIC, never HIT, and carry no age above 0.

Before a sale, run the same checks again under load: the Black Friday website checklist adds the load test, the cache warm-up and the four-week countdown for the traffic spike.

Common mistakes

  • Excluding /cart/ but not the translated slug. A store in Croatian or German serves the cart at /kosarica/ or /warenkorb/, which the exclusion misses. Exclude the slugs from Page setup, for every language a translation plugin adds.
  • Caching the Store API or wc-ajax. The Cart and Checkout blocks talk to /wp-json/wc/store/, and the classic cart to ?wc-ajax=. A CDN rule that caches API responses serves one shopper's cart to another even when the pages are excluded. Bypass both, as in the Cloudflare rule above.
  • Judging speed on the cached home page. It is the fastest page you have. Measure the cart, a search and a product page with a full basket too.
  • Deleting discontinued products without redirects. Every old link and ranking now lands on a 404. Redirect each deleted product before you empty the trash, or use the noindex route and delete later.
  • Dequeuing fragments on the cart page too. A snippet without the is_cart() condition stops the cart from updating after a quantity change. Keep the exceptions as in the snippet above.

Next in the series: what your WordPress plugins cost visitors, the plugin audit. The launch settings around the shop, from search visibility to the admin user, are in the 8 WordPress rookie mistakes.

Questions people ask

How do I make WooCommerce faster?

Cache every page except the cart, checkout and my-account, at every layer including the CDN, and bypass the cache for shoppers with a cart cookie. Stop the cart-fragments request outside the cart and checkout. Then check Time to First Byte on an uncached page: over 800 ms points to the host or PHP version. Finally, size product images to the layout and remove extensions that load on every page.

Why is WooCommerce slower than a normal WordPress site?

Because a store has pages that cannot be cached. The cart, checkout and account pages differ per shopper, so the server builds them on every request, and many setups skip the cache on every page once the basket has something in it. Stores also load more scripts, such as galleries, filters and cart fragments, and run more background jobs for orders and stock.

Which WooCommerce pages should never be cached?

The cart, the checkout and the my-account pages, including their translated slugs, plus the Store API under /wp-json/wc/store/ and any ?wc-ajax= request. Shoppers whose browser carries the woocommerce_items_in_cart or wp_woocommerce_session_ cookie should also bypass the page cache. Product, category and content pages should be cached; they are the same for everyone.

Does HPOS make WooCommerce faster?

It mainly speeds up the admin and order handling, not the pages shoppers see. High-Performance Order Storage moves orders out of the WordPress posts table into dedicated tables, so order searches, reports and checkouts that write orders do less work on a large store. New stores use it by default since WooCommerce 8.2; older stores should check extension compatibility before switching.

Is WooCommerce slow with a lot of products?

Not on cached product and category pages, which cost the same whether you sell 50 products or 50,000. Large catalogues slow the uncached parts: product search, layered filters, the admin product list and imports. Good hosting with an object cache, a search or filter plugin built for large catalogues, and deleting unused variations and old revisions keep them usable.

What should a WooCommerce store checklist include?

For speed: cart, checkout and account excluded from every cache, cart fragments only where needed, a fast server for uncached pages, and sized product images. For orders: a test purchase in two browsers. For search: complete Product schema with price and stock, a plan for sold-out products, and one SEO plugin. For safety: updates, staff two-factor login and backups.

Everything in WooCommerce

Check your site before and after Check