A persistent object cache keeps the results of WordPress database queries in Redis or Memcached so the next request can reuse them instead of asking MySQL again. It is worth adding when you have requests that cannot be page-cached — admin screens, logged-in users, a cart, REST calls, cron — and it is close to worthless on a brochure site where every visitor gets a cached HTML file from disk. Site Health will recommend one long before you need it, and that recommendation is the reason most people end up here.
The mechanism is simple enough to reason about. WordPress already caches query results in memory during a request through WP_Object_Cache; without a drop-in, that memory is thrown away when PHP finishes. A persistent backend gives the same API somewhere to write that survives the request. Nothing about your code changes. What changes is how many of the hundred-odd queries a page runs have to touch the database a second time.
Who this is for
What a persistent object cache actually does
WordPress looks for a single file: wp-content/object-cache.php. If it exists, wp_start_object_cache() requires it instead of loading core’s own cache.php, and from then on every wp_cache_get() and wp_cache_set() call in core, your plugins and your theme is served by that backend. That is the entire integration surface. It is a drop-in, not a plugin, which is why a Redis plugin can keep working after you deactivate it — and why a stale drop-in left behind by a deleted plugin is a classic white-screen cause.
Two details in core’s setup matter when you start measuring results. First, some groups are deliberately never persisted — core registers counts, plugins and theme_json as non-persistent, so those stay per-request no matter what you install. Second, a long list of groups is registered as global, including users, user_meta, site-transient, rss and theme_files; on multisite these are shared across the network rather than scoped per site.
The change you will notice first is to transients. Without a persistent backend, set_transient() writes a row to wp_options; with one, it goes to memory and never touches the table. On a site where a dozen plugins each cache an API response for an hour, that is a meaningful amount of write traffic and table churn removed. It also means the expiry you set is a ceiling, not a floor — core’s own documentation is explicit that a transient can vanish early, so any code that cannot regenerate its value is already broken and you are about to find out.
An object cache is not a page cache
Why Site Health recommends one, and when it is wrong
The check is not measuring your site’s speed. It reads a handful of counts and compares them to fixed thresholds, and it only runs when the environment type is production. WordPress suggests a persistent object cache if any one of these is true:
| Condition | Default threshold | Is it a real signal? |
|---|---|---|
| Site is Multisite | Always suggested | Usually yes. Global groups and shared lookups are exactly what an object cache is good at |
| Number of autoloaded options | More than 500 | Partly. It is a genuine symptom, but the fix is usually to trim the options, not to cache them |
| Serialized size of autoloaded options | More than 100,000 bytes | Same as above. Caching 2 MB of alloptions hides the problem rather than solving it |
Estimated rows in wp_users, wp_posts, wp_terms, wp_options or wp_comments | 1,000 or more in any one of them | Rarely on its own. A thousand rows is nothing for MySQL, and most real sites cross this in their first year |
information_schema estimates for InnoDB tables, so they are approximate by design.Look at that last row again. A thousand posts, or a thousand comments, or simply a thousand rows in wp_options — which plenty of sites reach from plugin settings alone — is enough to trigger the recommendation. That is why a small site with no performance problem gets told it should install Redis. The check is a reasonable default for a hosting platform that cannot inspect your traffic. It is not a diagnosis of your site.
If one of those counts is what tripped it, deal with the count first. An oversized alloptions payload is loaded on every single request, cache or no cache, and auditing and trimming WordPress autoloaded options is both faster to do and more useful than adding a Redis dependency on top of the bloat. The rest of what Site Health flags is worth working through in the same spirit — finding and fixing common Site Health problems covers the screen as a whole.
Core gives you filters if the recommendation is noise on a site you manage. site_status_persistent_object_cache_thresholds adjusts the numbers, and site_status_should_suggest_persistent_object_cache short-circuits the whole decision with a boolean. Hosts also have site_status_persistent_object_cache_url and site_status_persistent_object_cache_notes to point the recommendation at their own documentation.
<?php
// Raise the thresholds rather than silencing the test outright.
add_filter( 'site_status_persistent_object_cache_thresholds', function ( $thresholds ) {
$thresholds['posts_count'] = 20000;
$thresholds['comments_count'] = 20000;
$thresholds['options_count'] = 5000;
return $thresholds;
} );
Use that on a site you understand, not as a way to make a red panel go green on a site you have inherited.
What it speeds up, and what it does not
This is the part worth being blunt about, because the gap between the two columns is where most disappointment comes from.
| Request type | Does a persistent object cache help? | Why |
|---|---|---|
| Anonymous page served from a page cache | No | PHP never runs. There is nothing to cache |
| First request after a cache purge | Yes | The uncached rebuild is exactly the expensive path. This is what people mean by “faster after a deploy” |
| Logged-in front end — members, subscribers, customers | Yes, often substantially | These requests are excluded from page caching by design, so every one of them runs the full query load |
| WooCommerce cart, checkout, My Account | Yes | Never page-cached, and heavy on options, sessions and product lookups |
| wp-admin screens | Yes | No page cache applies, and list tables repeat the same meta and term queries |
| REST API and the block editor | Yes | Editor saves and sidebar panels are many small authenticated requests |
| WP-Cron and background jobs | Yes | Long-running tasks reuse the same options and terms thousands of times |
| Core Web Vitals for anonymous visitors | Usually no | LCP and CLS are dominated by assets, images and layout, not by server query count |
| A site starved of PHP workers or memory | No | It adds a network hop and a dependency. Fix the provisioning first |
Two consequences follow. If your complaint is that the dashboard crawls, an object cache may help but it is not the first thing to check — diagnosing a slow WordPress admin dashboard works through the causes in order of likelihood, and object caching sits well below autoloaded options and a jammed cron queue on that list. And if your complaint is a failing field score, diagnosing and fixing a failing WordPress INP score is the right page; INP is a main-thread problem and Redis cannot touch it.
How I would choose a backend
My criteria, stated before the table so you can disagree with them rather than with my conclusion:
| Option | Cost | Where it fits | Weaknesses |
|---|---|---|---|
| No object cache | Free | Mostly anonymous traffic behind a page cache; small sites; anything where you cannot monitor a second service | Every uncached request pays full query cost, and transients keep writing to wp_options |
| Redis Object Cache (free, by Till Krüss) | Free | The default choice. Version 3.0.0, 500,000+ active installs, tested to WordPress 7.1.3, needs WordPress 4.6+ and PHP 7.2+. Works with PhpRedis, Relay or the bundled Predis client | You still own the Redis server, its memory limit and its eviction policy. The metrics panel is basic next to a paid APM |
| WP Redis (free, by Pantheon) | Free | Smaller, more conservative drop-in. 9,000+ installs, version 1.4.7, tested to WordPress 6.9.10. Documents graceful fallback to the core object cache when Redis is unavailable | Much smaller user base, and tested against an older release than the free alternative above |
| Object Cache Pro (paid) | $95/month, or $950 billed yearly, for unlimited sites and environments under one owner; Enterprise from $1,950/month | Stores and large publishers where cache behaviour is a business risk. Vendor lists prefetching, analytics, TLS, health checks and WooCommerce-specific optimisations | Those are vendor claims, not figures I have measured. The price only makes sense if downtime or slow checkout costs you more than it does |
| Host-managed Redis or Memcached drop-in | Usually bundled, sometimes an add-on | Managed platforms that ship their own object-cache.php and support it | You inherit their flush behaviour and upgrade timing. Check what their drop-in does when the backend goes away |
For most sites the honest answer is the free Redis Object Cache plugin against a Redis instance the host already runs, or nothing at all. The paid tier is a real product solving real problems at scale; it is not where a 20,000-visit-a-month site should start.
Enable it and verify a persistent object cache is live
Do this on staging, and know your rollback
wp-content/object-cache.php. Know that before you start, because if the drop-in cannot reach Redis on a badly configured server, that file is the difference between a working site and a dead one. Have SSH or SFTP access open in another window.1. Check what you have now
# Which object cache implementation is active?
wp cache type
# Does the current implementation support flushing a single group?
wp cache supports flush_group
wp cache type reports Default on a site with no drop-in. If it already names something else, a host or a previous developer has set this up and your job is to find out what, not to install a second one.
2. Install the drop-in
# Install the plugin, then copy its object-cache.php into wp-content/
wp plugin install redis-cache --activate
wp redis enable
# Full diagnostics: client, status, prefix, hit ratio, memory
wp redis status
wp redis enable is the step that writes the drop-in. Activating the plugin alone does not start caching, which trips up plenty of people who then report no change. Run wp help redis for the rest of the subcommands on your installed version.
3. Set the constants that stop the common failures
<?php
// wp-config.php, above the "stop editing" line.
define( 'WP_REDIS_PREFIX', 'siteA_prod_' ); // Never share a prefix between sites
define( 'WP_REDIS_MAXTTL', 86400 ); // Cap how long any key can live
define( 'WP_REDIS_GRACEFUL', true ); // Serve the site if Redis is down
// define( 'WP_REDIS_DISABLED', true ); // Kill switch while debugging
WP_REDIS_PREFIX is the one you must not skip when more than one install shares a Redis database — staging and production on the same server is the usual way this bites. Without distinct prefixes, one site reads the other’s cached user and option data, and a flush on either wipes both. WP_REDIS_MAXTTL matters because WordPress sets plenty of cache entries with no expiry at all; a ceiling means a bad value eventually ages out instead of living until the next manual flush.
4. Confirm it is actually being used
# Should now report the drop-in's implementation, not "Default"
wp cache type
# Round-trip a value through the backend
wp cache set wpfairs_probe ok wpfairs 60
wp cache get wpfairs_probe wpfairs
# Flush one group rather than everything
wp cache flush-group wpfairs
If wp cache get returns your value in a second, separate CLI invocation, the cache is genuinely persistent — each WP-CLI run is its own PHP process, so a value surviving between them cannot be per-request memory. That two-command check is more trustworthy than any admin panel. Re-run Site Health afterwards and the recommendation should be gone.
Measure the gain instead of assuming it
Install Query Monitor on staging and record three numbers before you enable anything: total queries, total query time, and peak memory. Do it on the three pages that actually matter, which are almost never the homepage.
- A logged-in front-end page — an account page, a member-only post, a cart with two items in it.
- The heaviest admin list table — Orders, Posts filtered by a taxonomy, Users on a site with real user counts.
- One warm repeat of each, because the first request after enabling the cache populates it and will look no faster at all.
Then enable the drop-in, load each page twice, and compare the second load. A result worth keeping looks like a clear drop in query count and query time on the warm request. A result that is not worth keeping looks like a handful of queries saved and the same wall-clock time — which means your bottleneck was never the database, and you have just added a service to monitor for nothing.
Check hit ratio in wp redis status after a day of real traffic, not after five minutes of your own clicking. A low ratio alongside high eviction counts usually means Redis has less memory than your working set needs, and the fix is a bigger maxmemory value, not a different plugin. If the box is already short on RAM, that is a hosting conversation — WooCommerce hosting requirements: PHP workers and RAM covers how to size this honestly for a store.
What goes wrong after you enable it
Redis restarts and the site dies. The drop-in’s behaviour when the backend is unreachable decides whether this is an outage or a slowdown. Set the graceful option, then prove it by stopping Redis on staging and loading the site. Test this before you need it.
Eviction under memory pressure. A Redis instance with maxmemory-policy noeviction starts refusing writes when it fills up, and the drop-in starts throwing errors. allkeys-lru drops old keys instead, which is almost always what a WordPress object cache wants, since every entry is regenerable by definition.
Stale data that looks like a plugin bug. If a change in the admin does not appear on the front end, flush the object cache before you start bisecting plugins. Conversely, if flushing fixes it every time, you have a plugin caching something without invalidating it, and that is worth reporting upstream rather than papering over with a scheduled flush.
An orphaned drop-in. Delete the plugin without running its disable command and wp-content/object-cache.php stays behind, pointing at code that no longer exists. That is a fatal error on every request. If you are mid-incident and the site is blank, deleting that one file is the first thing to try — how to fix the WordPress white screen of death covers working through it when the dashboard is gone too.
Staging and production sharing one database. Covered above, and worth repeating because it is silent. Distinct prefixes, or distinct Redis databases, every time.
So should you add one?
- Brochure or blog site, mostly anonymous traffic, page cache in place: no. Silence the Site Health notice with the threshold filter if it bothers you, and spend the effort on images and asset delivery instead.
- Site Health flagged autoloaded options: trim the options first. If the warning was the only reason you are here, you may find you no longer need the cache.
- WooCommerce store taking real orders: yes, with managed Redis if the host offers it. Cart, checkout and My Account cannot be page-cached, and those are the pages that cost you money when they are slow.
- Membership site, forum, LMS — anything where most visitors are logged in: yes. This is the clearest case on the page, because page caching barely applies to your traffic at all.
- Multisite network: yes, and set per-site key prefixes before you go anywhere near production.
- Shared hosting with no Redis and no way to monitor one: no. A cache you cannot observe or restart is a liability, and a pure-PHP client on an overloaded box can be slower than the queries it replaces.
- You are not sure which of these you are: measure on staging with Query Monitor. Twenty minutes of before-and-after numbers settles it better than any article, including this one.
Whatever you decide, write down that the drop-in exists and which plugin owns it. The most common object-cache incident I see is not a cache failure — it is a developer two years later deleting a plugin they do not recognise and taking the site down with an orphaned object-cache.php nobody documented.
Frequently asked questions
Do I need a persistent object cache if I already use a caching plugin?
They are different layers. Most caching plugins do page caching, which skips PHP for anonymous visitors; an object cache speeds up the requests that page caching cannot serve, such as the admin, logged-in users and a cart. Some suites bundle both, so check what yours has actually enabled before adding a second drop-in.
How do I check whether my site already has one?
Run wp cache type, or look for a file at wp-content/object-cache.php. Site Health tells you too: if the persistent object cache recommendation is absent on a site big enough to trigger it, something is already providing one. Managed hosts frequently install a drop-in without mentioning it.
Redis or Memcached for WordPress?
Take whichever your host runs and supports. The practical difference for WordPress is ecosystem rather than raw speed: the maintained free drop-ins and the commercial options are overwhelmingly Redis-based, so Redis is the better-trodden path. Memcached is a perfectly good backend if your platform already provides and monitors it.
Will it improve my Core Web Vitals?
For anonymous visitors on a page-cached site, almost certainly not — those requests never ran PHP in the first place. It can improve TTFB on uncached and logged-in requests, which feeds into LCP for those users. INP is a browser main-thread metric and server-side caching does not touch it.
Where do my transients go once an object cache is active?
Into the cache backend instead of wp_options. That removes a lot of database writes, and it means a cache flush deletes every transient at once. Core’s documentation is clear that transient expiry is a maximum with no guaranteed minimum, so any code that assumes a transient will still be there is already fragile.
Is it safe to flush the object cache on a live site?
Safe in that nothing is lost — every entry is regenerable. Not free, though: a full flush on a busy site means every request rebuilds its queries at once, which is a load spike. Prefer wp cache flush-group where the implementation supports it, and avoid flushing at peak times.
How do I remove a persistent object cache cleanly?
Disable the drop-in through the plugin first, which deletes wp-content/object-cache.php, then deactivate and delete the plugin. If you have already deleted the plugin and the site is down, remove that file over SFTP or with rm; WordPress falls straight back to its per-request cache.
Sources
- wp_start_object_cache() — drop-in path, loading behaviour, and the default global and non-persistent cache groups
- WP_Site_Health::should_suggest_persistent_object_cache() — the exact conditions and thresholds quoted above
- New cache Site Health checks in WordPress 6.1 — the four filters and the production-environment restriction
- wp cache — the
type,supports,flush-groupand get/set subcommands used here - Transients API — where transients are stored with and without a persistent cache, and why expiry is a maximum
- Redis Object Cache and WP Redis on WordPress.org — versions, install counts, supported clients and constants
- Object Cache Pro pricing — the paid tier figures quoted in the comparison table

Leave a Reply to This Post