Case Study: WooCommerce Page Caching Across 42 Currencies

There are stores where a caching mistake costs a few milliseconds, and stores where it costs the day’s orders. sculpiflex.com is the second kind: a high-traffic direct-to-consumer store doing around 7 million views a month, selling worldwide through Meta and Google ads, on WordPress and WooCommerce, in 42 currencies. I have worked on it through the MIDAGENCY agency since July 2025, and the engagement continues today as ongoing maintenance.
This is the story of its page cache: why the store’s own currency switcher was quietly defeating it, and the per-currency architecture that finally made caching safe on traffic where every mistake is paid for twice, once in ad spend and once in lost orders.
The brief and the constraint
On an ads-driven store, every landing URL arrives carrying campaign parameters. That is the normal paid-traffic problem, and by itself it is fixable. The harder constraint was what cache experiments did to the business when they went wrong: on this traffic, the damage showed up same-day, as a conversion crash.
One cache experiment did exactly that. It took a Spanish campaign’s conversion rate to near zero for a day. And the failure mode the client weighed before allowing any further cache work was the persistent version of the same mistake: a wrong-currency page served from cache would have halved that conversion rate.
The wrong-currency class was no hypothetical either: on 23 September 2026, a client-side report from Japan recorded the switcher on MXN while prices rendered in GBP, the documented instance that preceded and prompted the fragment-cache build below.
The client’s own framing of the job: any live test on this traffic costs money, and a conversion gain of a single percent covers the entire server budget. That arithmetic set the rules for everything that followed. The cache had to be made safe first. Fast came second.
The diagnosis
The starting measurement: TTFB sat around 2 seconds, and that server wait accounted for roughly 70% of the LCP failure. On a store this size that number is not a vanity metric. It is the page opening late for the exact visitors the campaigns paid for.
The root cause was structural, not a stray plugin setting:
-
A geolocation currency switcher busting the page cache per currency. The switcher exists to show every visitor prices in their own currency, one of 42. The page cache could not safely answer for a currency it had not stored, so each visitor’s arrival forced a rebuild. The store’s own personalization was defeating its cache.
-
Checkout 504 storms at the FunnelKit checkout during campaigns. Under campaign load the checkout path stacked timeouts, and each storm meant shoppers who had already decided to buy were staring at a dead page. The traced root cause was a single Apache line: the global
Timeout 30(withProxyTimeoutdefaulting to 30 seconds) killed every PHP request that ran longer than 30 seconds; the fix unified timeouts to 60 seconds across Apache, nginx, and PHP-FPM. A FunnelKit order-lookup query also hit the slow log 798 times in 18 days at 2.5 to 24 seconds per run, until oneANALYZE TABLEagainst stale InnoDB statistics took it from 22.4 to 0.38 seconds on 7 August 2026, with no schema change. -
Cache-control conflicts between the CDN and the store. The two layers disagreed about what could be cached and for how long, which made every other fix unreliable: a correct rule at one layer was overruled at the other.
One constraint had to be respected throughout, and it is worth stating honestly because it rules out the obvious “just cache everything” answer. Cart and checkout pages are uncached on purpose. They show the shopper’s own cart, their own currency, and their logged-in state, so serving one cached copy to everyone would be a different disaster. The fix had to make the cache safe for the pages that can be cached, without touching the ones that cannot.
What was actually done
-
A per-currency page-cache architecture. The cache was restructured so each currency is a first-class cache variant. The currency switcher stopped busting the cache, because the cache could now answer correctly for every currency it had seen instead of refusing to answer at all. The shipped shape is two layers: the page cache itself is currency-cookie-aware (a cache helper varies it on the currency-switcher cookie), and a per-country/currency fragment cache at the server edge caches the switcher’s responses.
-
Cache-control conflicts resolved between the CDN and the store, so both layers agree on what is cached, for whom, and for how long.
-
The FunnelKit checkout stabilized. The 504 storms during campaigns ended.
-
A migration to Cloudflare and Hetzner, putting the store on infrastructure that could hold the new caching behavior under campaign traffic.
The receipt
Cache recoveries attract loose talk, so here is what the record shows:
-
TTFB came down from ~2s to sub-1s.
-
The cache became safe to test on live ads traffic. That is the operational change that matters most on this store: cache work stopped being a gamble with the day’s orders.
-
Conversion crashes are gone from the log. No further same-day crashes followed the architecture change.
-
The fragment cache is measured, not assumed. Initial telemetry showed 0.0% of switcher AJAX calls eligible for reuse (visitor IP inside the request body, per-visitor cookies on top); a shadow measurement with the IP normalized out showed 58.7% potential reuse; the deployed cache measured a 63.65% live hit ratio the next day, 1,131 hits of 1,777 cacheable calls.
-
CPU down 20-25% at equal traffic (owner-measured 24 September 2026, after the fragment cache went live).
-
Steady-state cached TTFB around 0.1 seconds (0.09 to 0.11s across recent cache-warmer passes), the measured floor under the sub-1s figure above.
There is no conversion percentage and no revenue figure in this receipt, and that is deliberate. On a store where even a 1% improvement matters, the honest report is the one above.
What held
The engagement never closed out into a handover. Since July 2025 it has run as ongoing maintenance, which on a store like this is its own verdict: campaigns, catalog changes, and new ad creative keep arriving, and the cache architecture has kept answering them safely. The work that held is precisely the part nobody photographs: a checkout that survives campaign nights, and a cache that stays boring.
Safe to test is a discipline with dates on it. The first shared fragment-cache attempt was disabled the same night it went live, after a cache hit replayed a visitor’s Set-Cookie header; the corrected, scoped version was verified miss-to-hit with zero Set-Cookie, and then one more trap surfaced and was fixed: an anonymous WooCommerce session cookie was silently busting the cache. And earlier that month, on 7 September 2026, the page cache and the Cloudflare edge served a real shopper’s cart to anonymous visitors (one anonymous visitor saw a quantity-5, $466.60 cart on a product page); 14 poisoned cache files were scrubbed, the edge was purged, and a cart-cache guard shipped so cached HTML cannot carry one shopper’s cart to another. As this case study publishes, a second-server migration is running in a deliberately chosen low-traffic window with the ad campaigns paused.
Need this kind of work on your site?
If your store sells in multiple currencies and your page cache keeps missing for the visitors who matter, that is the exact shape of the engagement above: a cache made safe first, then fast, on traffic where mistakes are paid for in real money. Get in touch and we’ll look at your numbers together. The WooCommerce portfolio page carries this store’s full arc, and the Ads & Conversion page has the spend-side receipt for it, priced in click costs.