If you’ve run a WooCommerce store with any real catalog size for more than a year, there’s a decent chance your wp_options table is bigger than it should be — and a REST caching plugin is often the reason why.
The pattern is common enough to have a shape you can recognize on sight: a plugin caches REST responses using the WordPress Transients API, because transients are the obvious built-in tool for “store this, expire it later.” Without an external object cache configured, every one of those transients lands as a row in wp_options. Product lists, taxonomy trees, paginated collections, one row per cached response, accumulating for as long as the site runs.
Nothing prunes them automatically unless something goes looking. WordPress only clears a transient when it’s read past its expiration and something requests it again — an unread, expired transient can sit in the table indefinitely. On a catalog with thousands of products, variations, and category permutations, that adds up to gigabytes of rows in a table that was never designed to hold response payloads.
wp_options is a configuration table. Every autoloaded row on it gets pulled into memory on nearly every request — whether or not that particular request needed it.
That’s the part that actually hurts. It’s not just disk space. A bloated wp_options table means slower backups, slower migrations, and — if any of those cache rows are autoloaded — extra weight on the one database query WordPress runs on almost every page load. The usual remedy is to bolt on Redis or Memcached so transients get diverted out of MySQL. That’s a reasonable fix for the symptom. It doesn’t change the fact that the cache and the site’s configuration data are still competing for the same storage mechanism.
1. A different place to put a cache
Frosty Cache starts from a different premise: a REST response cache isn’t configuration data, so it shouldn’t live next to configuration data. Instead of transients, cached responses are written as JSON files under wp-content/uploads/frostycache_cache/. The database never sees them. There’s no row to autoload, no table to bloat, and no dependency on an external object cache just to keep the site’s core tables lean.
Transient-based
- Cache lives in
wp_options - Every cached response is a database row
- Grows without a built-in ceiling
- Expired rows can linger unless re-requested
- Needs Redis/Memcached to avoid table bloat
File-based
- Cache lives on disk
- Cached responses are JSON files, not rows
- Database size is unaffected by cache volume
- A scheduled sweep clears expired files directly
- No object cache required to stay lean
Reads get faster along with it. Cached responses are served through an advanced-cache.php drop-in, the same early-loading mechanism page-caching plugins use, so a cache hit can return before WordPress finishes booting. There’s no plugin stack, no database connection, and no query to run on the request path at all.
2. Freshness without waiting on a clock
A time-based expiry is a blunt instrument. Set it short and you’re regenerating cache constantly; set it long — which is the usual advice, since duration barely affects total cache size — and stale data sits around until something happens to notice. Frosty Cache treats TTL as the fallback, not the primary mechanism. The default path is event-driven: when the content underneath a cached response actually changes, the cache file for it is deleted immediately.
save_post – Editing a post, page, or taxonomy term clears the responses that reflect it.
Reusable block edits – A reverse index tracks which cached posts embed a given block, so editing the block invalidates every post that uses it.
woocommerce_checkout_order_processed – When an order completes, cached product responses for the purchased items are cleared — so stock counts don’t wait out a TTL to catch up with a sale.
A twicedaily cron sweep handles the rest: anything that expires without a matching event still gets cleared on schedule, so the fallback stays a backstop rather than the main mechanism.
3. The cart is not the catalog
Caching REST responses on a WooCommerce store means drawing a hard line between what’s public and what’s personal, and getting that line wrong in either direction is a real problem: cache the cart and one shopper starts seeing another’s line items; cache nothing and you’ve paid the database cost for no benefit.
Frosty Cache draws that line at the route. Cart, checkout, order, and customer endpoints — on both the core WooCommerce Store API and CoCart — are excluded outright, matched by route pattern before a response is ever considered for caching. That holds even for an anonymous visitor who technically isn’t “logged in” but still carries session-specific cart data. Only the genuinely public surface — products, categories, tags — goes into the cache.
4. Warming the cache before traffic finds it
A cold cache and a traffic spike are a bad combination: the first wave of requests all miss at once and hit full WordPress execution simultaneously. Frosty Cache ships a WP-CLI command to prime the cache ahead of time, issuing real requests for posts and products so the files already exist before visitors do.
$ wp frostycache warm $ wp frostycache status $ wp frostycache cleanup
None of this is about caching REST responses harder — it’s about not making the database carry a job it was never suited for in the first place. If you’re running WooCommerce or CoCart behind a decoupled front end and want your REST cache to behave like a cache instead of a slowly growing table, that’s the problem Frosty Cache was built around.