Panellicense

LiteSpeed ESI hole-punching for WooCommerce carts

Cache the full page for everyone while keeping the WooCommerce cart, "Hi, name" greeting, and nonce fields live — set up ESI blocks in LiteSpeed Cache the right way.

7 min readUpdated 2026-06-01litespeed · lscache · esi · woocommerce
schema: HowToschema: FAQPageschema: BreadcrumbList

The usual reason a WooCommerce site shows a 2% cache hit rate is that LSCache has been told to bypass the cache for every logged-in user and every visitor with a cart cookie. That's the safe default, and it throws away most of the benefit you bought LiteSpeed for. ESI — Edge Side Includes — is the fix: the server caches the page shell once and stitches in a handful of dynamic fragments (cart total, login greeting, security nonce) on every request.

This guide is for operators running WooCommerce or membership sites on LiteSpeed Web Server who already have LSCache working on static pages but see it disabled the moment someone adds to cart. It covers what ESI actually does, the three blocks WooCommerce needs punched, and how to debug a fragment that won't update.

How ESI changes the caching model

Without ESI, LSCache makes a binary decision per request: serve the whole page from cache, or run all of PHP and cache nothing. A woocommerce_items_in_cart cookie flips it to the second branch, so the entire catalogue page rebuilds for a shopper who added one item.

ESI splits the page into a cacheable parent and one or more independently cached (or uncached) child blocks. The parent — product grid, header, footer, sidebar — caches with a long TTL and serves to everyone. The cart widget becomes an ESI block with its own short TTL or no cache at all. LiteSpeed assembles the response inside the server binary before it hits the wire, so the browser never sees the seams.

The mechanism is a placeholder comment in the HTML:

<esi:include src='/?lscache_esi=storefront_cart' cache-control='no-cache'/>

LSWS intercepts that tag, fetches the fragment (running only the PHP needed for the cart), and substitutes the result. One PHP call for a small widget beats rebuilding the full page.

Enable ESI in the LiteSpeed Cache plugin

ESI is off by default. Turn it on per site in the WordPress admin:

  1. LiteSpeed Cache → Cache → ESI.
  2. Set Enable ESI to ON.
  3. Set Cache Admin Bar to ON — the admin bar is the classic ESI block for logged-in users and the simplest one to verify against.
  4. Leave Vary Group at defaults unless you run membership tiers (more on that below).

Save, then purge the whole cache (LiteSpeed Cache → Toolbox → Purge All). The plugin ships WooCommerce ESI templates, so once ESI is on, the mini-cart and the "Hi, name" account greeting are punched automatically — you don't hand-write the esi:include tags for the bundled widgets.

Confirm the server actually processed the tag rather than leaking it to the browser:

curl -s https://shop.example.com/shop/ | grep -c 'esi:include'

A correctly configured server returns 0 — the tags were resolved server-side. If you see a non-zero count, ESI is enabled in the plugin but the server isn't honouring it; check that you're on LSWS Enterprise and not Apache, and that Module cache is loaded in WHM → LiteSpeed Web Server → LSWS configuration.

The three blocks WooCommerce needs

Mini-cart and cart fragments

WooCommerce updates the cart count over AJAX (?wc-ajax=get_refreshed_fragments), which is itself uncacheable. With ESI on, the LiteSpeed plugin replaces the mini-cart widget with an ESI block so the page caches and the cart number stays correct. If your theme renders the cart total somewhere outside the standard widget (a custom header), that copy won't update — you must wrap it yourself (see the next section).

Login state and the account greeting

Any "My Account" / "Hi, name" element is per-user. The plugin's storefront_cart and account ESI templates handle Storefront and most well-behaved themes. Page builders that inject the greeting via a shortcode often miss it — symptom: the cached page shows the previous visitor's name. That's the highest-severity ESI bug and the reason to test logged-in rendering with two different accounts before going live.

Nonces in forms and AJAX

WordPress nonces expire (default 12–24 hours). If a nonce is baked into a page cached for a week, form submissions start failing with "Are you sure you want to do this?" once the nonce ages out. The LiteSpeed plugin ships an ESI block for the WooCommerce add-to-cart nonce; for custom forms, register the nonce field as its own non-cached ESI block so a fresh token renders on every load.

Punch a custom block by hand

For theme elements outside the bundled templates, wrap the dynamic part in the plugin's ESI shortcode in your child theme or a snippet:

echo do_shortcode(
  '[esi cache="no-cache" ttl="0"]' .
  '[your_dynamic_shortcode]' .
  '[/esi]'
);

Or, for a fragment that's the same for all anonymous visitors but should refresh hourly, give it a short TTL instead of no-cache:

echo do_shortcode('[esi ttl="3600"]' . '[live_exchange_rate]' . '[/esi]');

Purge all after editing. Then re-run the grep -c 'esi:include' check — it should still return 0 from the public side.

Vary groups for membership tiers

If logged-in users should see different cached pages by role (free vs. premium), set the Vary Group values under Cache → ESI. Assign each role an integer:

administrator    99
premium-member   5
subscriber       2

LSCache then keeps a separate cached copy per vary group rather than serving uncached pages to every member. This is what turns a membership site from "cache disabled for all logged-in traffic" into "cache hit for everyone in the same tier" — often the single biggest TTFB win on a paywalled WooCommerce store. Pair it with an object cache via LSMCD so the per-group PHP that does run isn't hammering MySQL.

Verify hit rate after the change

Load a product page twice and read the response header:

curl -sI https://shop.example.com/shop/?add-to-cart=99 | grep -i x-litespeed-cache
  • x-litespeed-cache: hit — the parent served from cache; ESI punched the cart. Working.
  • x-litespeed-cache: miss,no-cache on every load — ESI isn't engaging; the page is still being bypassed wholesale. Re-check that the cart cookie rule wasn't left in Cache → Excludes.

If pages that used to bypass now report hit, your hit rate on a busy store typically jumps from low single digits to 70–90%, and PHP worker pressure drops accordingly. If the cache is still disabled, work through the LSCache disabled on WordPress checklist first — ESI can't help a site that isn't caching at all.

Does LiteSpeed ESI require a paid license?+
ESI ships with LiteSpeed Web Server Enterprise, which every LSWS license includes. The LiteSpeed Cache WordPress plugin that exposes ESI is free. OpenLiteSpeed supports ESI too, but without the cPanel-integrated plugin tooling.
Why is my WooCommerce cart showing the wrong total with ESI on?+
The cart total in your theme is being rendered outside the bundled mini-cart widget, so it was cached in the parent page. Wrap that element in an [esi cache="no-cache"] block, or switch the header to the standard WooCommerce mini-cart widget that the plugin already punches.
Can ESI cache different content for logged-in users?+
Yes. Assign each WordPress role a Vary Group integer under Cache → ESI. LSCache keeps a separate cached copy per group instead of bypassing the cache for all logged-in traffic.
Does ESI work with full-page CDN caching like QUIC.cloud?+
ESI assembly happens on your origin LiteSpeed server, so the CDN should cache the assembled HTML or be configured to defer dynamic blocks. See the QUIC.cloud setup guide for how the two layers interact.
How do I confirm ESI tags aren't leaking to visitors?+
Run curl on a public URL and grep for 'esi:include'. The count should be 0 — a non-zero result means the server isn't resolving the tags, usually because the site is on Apache/mod_lsapi rather than LSWS.

Next steps

Switch in an afternoon

Switch from your current reseller — free.

We migrate active cPanel, Plesk, LiteSpeed and CloudLinux licenses from any reseller. We prorate the first month so you never pay twice, and your customers see zero downtime during the swap.