WordPress Object Cache: Redis, Memcached, or Neither

Install Redis, refresh the homepage, and the timing looks identical. That is the expected result on a site where most visitors receive a cached HTML file. The object cache did not fail; it was aimed at a different layer.

The layer it serves is the database. WordPress builds every uncached page by running queries, and the object cache keeps the results of those queries in memory so the next request can skip them. On a logged-in dashboard, a cart, a checkout, or a query-heavy archive, that can matter. On a cached article page, it usually does not.

The decision is not “Redis or Memcached.” It is whether a persistent object cache has enough uncached work to justify another service that can fail. This is how to measure that, configure it if the answer is yes, and recognize when the answer is no.

What the Object Cache Actually Stores

WordPress has an object cache in core. WP_Object_Cache holds data in memory for the duration of a request, keyed by a name and a group. Options, posts, terms, and query results all pass through it.

By default that cache is non-persistent: the memory is released when the request ends. The WP_Object_Cache documentation is explicit about this, and about the consequence for transients. Without a persistent backend, transients fall back to the options table. With one, transients use the object cache.

A persistent object cache replaces the core class with a drop-in at wp-content/object-cache.php. That file connects to a shared memory service — Redis, Memcached, or something else — and keeps entries between requests.

It does not cache the rendered HTML. That is a page cache. It does not compile PHP. That is OPcache. It does not put files on edge servers. That is a CDN. The WordPress cache overview separates these layers, and the separation is the reason a Redis install can leave your Largest Contentful Paint unchanged.

LayerStoresTypical win
Page cacheRendered HTMLRepeat anonymous visits
Object cacheQuery results, options, termsUncached and dynamic requests
OPcacheCompiled PHPEvery PHP request
CDNStatic assets, sometimes HTMLDistance and origin load

If your page cache serves most anonymous traffic, the object cache is competing for a smaller share of requests. That share can still be valuable — logged-in customers, admins, REST API calls — but it is a different business case.

The WP_CACHE Myth

Some hosting guides still say that adding define( 'WP_CACHE', true ); enables the object cache. It does not. The WordPress documentation states that this was true before WordPress 2.5 and has not been true since.

WP_CACHE is used by page-cache drop-ins such as advanced-cache.php. It tells WordPress to load that file early. It has nothing to do with whether object-cache.php exists or whether Redis is reachable.

The confusion is expensive because it produces a false positive. A site can have WP_CACHE set, a page cache working, and no persistent object cache at all. The admin bar may show a cache hit, and the database is still doing every query on every uncached request.

Check the actual state instead of the constant:

wp eval 'var_dump( wp_using_ext_object_cache() );'
ls -l wp-content/object-cache.php
wp redis status

The first command returns true only when an external object cache is in use. The second confirms the drop-in exists. The third is specific to the Redis Object Cache plugin and prints its connection diagnostics, as documented in the plugin repository.

If the first command returns false, the site is using core’s per-request cache. That is not a bug. It is the default, and on many sites it is enough.

Prove Persistence Across Processes

A cache that works inside one request is not a persistent cache. The clean test is to write a key in one WP-CLI process and read it in another.

wp cache set oc_probe alive
wp cache get oc_probe

If the second command prints alive, the value survived between processes and a persistent backend is working. If it prints nothing or false, the site is using the per-request cache.

Two caveats. WP-CLI may load a different configuration than the web server, especially on hosts that inject Redis credentials through the web environment. And a site can have a working object cache that WP-CLI bypasses through WP_REDIS_DISABLED or a missing PHP extension. Test one real uncached URL with Query Monitor as well before concluding the cache is broken.

Redis or Memcached?

Both are in-memory key-value stores, and WordPress needs very little from either: get, set, delete, increment, and flush, with expiration. The differences are operational.

Redis can persist data to disk with RDB snapshots or an append-only file, and it can run with persistence disabled entirely. The persistence documentation lists all three modes and notes that no persistence is sometimes used for caching. Redis also exposes an explicit memory limit and eviction policy.

Memcached is described by its project as an in-memory key-value store for small chunks of data, built to alleviate database load. It is a pure cache: restart the service and the data is gone. That is not a defect for WordPress, because every cached value should be regenerable. The project page is short and worth reading for what it does not claim.

The practical choice usually comes down to what your host provides and who monitors it:

  • Choose Redis when you already have a managed instance, when you want an explicit eviction policy and memory ceiling, or when the same service may later hold sessions or queues. The Redis Object Cache plugin is the common WordPress integration.
  • Choose Memcached when it is already provisioned, when the workload is a simple shared key-value cache across multiple application servers, and when you do not need persistence.
  • Choose neither when the page cache already absorbs the requests that matter, or when nobody will watch memory, eviction, and connection errors.

There is no performance prize for picking the more complicated service. A well-sized Memcached instance with a high hit rate beats an undersized Redis instance that evicts constantly.

Configure Redis So a Failure Is Boring

The Redis Object Cache plugin reads its connection settings from wp-config.php. Keeping them there, rather than in a database option, means the configuration travels with the site and can be changed without logging in.

define( 'WP_REDIS_HOST', '127.0.0.1' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_PASSWORD', getenv( 'WP_REDIS_PASSWORD' ) ?: '' );
define( 'WP_REDIS_DATABASE', 0 );
define( 'WP_REDIS_PREFIX', 'awplife:' );
define( 'WP_REDIS_TIMEOUT', 1 );
define( 'WP_REDIS_READ_TIMEOUT', 1 );
define( 'WP_REDIS_MAXTTL', 0 );
define( 'WP_REDIS_DISABLED', false );

The defaults and descriptions come from the plugin’s configuration table. Four of these deserve a comment.

The prefix is not decoration. If two WordPress sites share one Redis instance without a unique WP_REDIS_PREFIX, their keys collide. A flush on one site can break the other, and cached options can cross domains. Use a human-readable prefix per site, and use a separate database if the host gives you one. The plugin documents databases 0–15.

The timeouts are a availability setting. If Redis becomes unreachable, PHP waits for the connection. A one-second connect timeout and a one-second read timeout turn a dead cache into a slow request rather than a hung one. Do not raise them to thirty seconds because a graph looked better.

WP_REDIS_MAXTTL caps the life of every key. Zero means no cap. A cap can protect memory, but it also forces regeneration on a schedule and can create a stampede if a large group expires together. Leave it at zero unless you have a reason and a measurement.

WP_REDIS_DISABLED is the emergency switch. Set it to true to bypass the object cache without deleting the drop-in. That is faster and safer than renaming files over SSH during an incident.

None of this configures the Redis server itself. The server still needs a memory limit and an eviction policy.

Set the Eviction Policy for a Cache, Not a Database

Redis with no memory limit will grow until the host runs out. Redis with a limit and the wrong policy will refuse writes or evict data you wanted to keep.

The eviction documentation explains the options. noeviction returns an error when a write would exceed maxmemory. For a cache, that is the wrong failure: WordPress can regenerate an evicted value, but it cannot store a new one when the server refuses.

For a Redis instance used only as a WordPress object cache, allkeys-lru is a reasonable default. It evicts the least recently used keys across the whole dataset, which matches the access pattern of a cache. If the instance also holds data that must not be evicted, do not mix it with the cache; use a separate database or instance.

Check the current settings rather than assuming:

redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy
redis-cli INFO memory | grep used_memory_human
redis-cli INFO keyspace

If maxmemory is 0, there is no ceiling. If the policy is noeviction, a full instance will produce write errors. If used_memory_human is close to maxmemory, the next traffic spike decides which keys survive.

Measure Hit Rate Before You Add Memory

Hit rate tells you whether the cache is doing work or just occupying RAM. Redis exposes it in INFO stats:

redis-cli INFO stats | grep -E 'keyspace_hits|keyspace_misses'

The ratio is hits / (hits + misses). A high ratio means most lookups are served from memory. A low ratio means the cache is either too small, the keys are too short-lived, or the workload is not repetitive enough to cache.

Use a dedicated Redis instance or database for a clean number. If other applications share the server, their hits and misses are included in the same counters.

For Memcached, the same idea comes from stats:

echo stats | nc 127.0.0.1 11211 | grep -E 'get_hits|get_misses|curr_items|limit_maxbytes'

get_hits and get_misses answer the same question. limit_maxbytes shows the memory ceiling. curr_items shows whether the cache is holding anything at all.

Hit rate alone is not a verdict. A cache can have a high hit rate and still save very little time if the cached queries were cheap. Pair it with the query timing below.

Measure the Work the Cache Replaces

The object cache saves database time. If database time is not a meaningful share of your response, the cache cannot produce a meaningful speedup.

Query Monitor is the fastest way to see this on a single request. Open an uncached page, look at the query count and total query time, then compare it with the page generation time. If queries are a small slice, an object cache is not your bottleneck.

For a week-long baseline across many requests, log query count and query time from a must-use plugin. Put this in wp-content/mu-plugins/object-cache-check.php and enable SAVEQUERIES in wp-config.php for the measurement window only.

add_action(
    'shutdown',
    function () {
        if ( ! defined( 'SAVEQUERIES' ) || ! SAVEQUERIES ) {
            return;
        }

        global $wpdb;

        $count = count( $wpdb->queries );
        $time  = 0.0;

        foreach ( $wpdb->queries as $query ) {
            $time += (float) $query[1];
        }

        $uri = isset( $_SERVER['REQUEST_URI'] )
            ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) )
            : '-';

        error_log(
            sprintf(
                'object-cache-check queries=%d time=%.4f uri=%s',
                $count,
                $time,
                $uri
            )
        );
    },
    999
);

SAVEQUERIES stores every query in memory, so this is a diagnostic, not a permanent setting. Run it on staging for a day, or on production during a quiet window, then remove the constant.

Compare the median query count and query time for the same template before and after enabling the object cache. The average is misleading because a few admin requests can dominate it.

What a Good Result Looks Like

A useful object cache changes the shape of the uncached request, not the cached one.

  • Query count falls on repeated requests. The first request after a flush populates the cache; the next one should skip the same reads.
  • Query time falls more than total page time. The page still has to render, so a 40% query-time reduction may be a 5% response-time reduction.
  • Hit rate is high on the pages that use it. A cache that misses on most lookups is not earning its memory.
  • Error rate does not move. A cache that improves the median and worsens the tail is a net loss.

The last point is the one that gets ignored. Redis on the same host as PHP competes for memory. Redis across the network adds a round trip. If the cache is undersized, eviction churn can make the tail worse than the uncached baseline.

When Redis Is the Right Answer

Redis earns its place when the uncached workload is large and repetitive.

Logged-in users. Admin screens, account pages, and member dashboards bypass a page cache by design. They run the same option and query patterns on every request.

WooCommerce dynamic routes. Cart, checkout, and account pages are not static HTML. A store with a large catalog also runs repeated product, term, and lookup queries that a shared object cache can hold. The WooCommerce scaling guide covers the database and queue side of that workload.

High-query plugins. Page builders, related-post modules, and reporting plugins can issue the same queries across many requests. Caching those results helps even when the page itself is not cached.

Multisite. Many sites share options and network queries. A persistent cache reduces repeated reads across the network, provided each site uses a distinct prefix or database.

REST and API traffic. Requests that bypass the page cache still benefit from cached options and terms.

In each case, the test is the same: measure the uncached requests that matter, then measure again after the cache is on.

When Memcached Is the Right Answer

Memcached is the right answer when it is already there and the job is a shared key-value cache.

It is simple to run, it distributes across multiple application servers, and it does not pretend to be durable. If a node restarts, the cache repopulates from the database. That is exactly the behavior WordPress expects from a cache.

It is a poor fit when you need persistence, data structures beyond strings, or a service that can also act as a queue. Those are Redis features, and adding them to Memcached is the wrong direction.

The cache plugin roundup is the hub for the surrounding page-cache and object-cache options; this article is about the decision, not the plugin list. If you are still choosing a page-cache layer, the free cache plugin comparison covers that separate choice.

When Neither Is the Right Answer

This is the most common correct answer, and it is the one hosting companies rarely sell.

The page cache serves almost everything. If anonymous visitors get static HTML and the uncached share is small, the object cache improves a minority of requests. Measure that minority before paying for another service.

Query time is already small. A page that generates in 120 ms with 15 ms of database time will not be transformed by caching those queries. The remaining time is PHP, rendering, and network.

The host cannot give you a reliable instance. A Redis service that is oversubscribed, evicted by a neighbor, or restarted without warning adds incidents. A slow cache is worse than no cache.

Nobody will monitor it. Object caches fail in quiet ways: memory fills, hit rate falls, timeouts rise. If no one owns the dashboard, the cache becomes an unattended dependency.

The site is small and stable. A brochure site with a good page cache and no logged-in traffic has little uncached work. Core’s per-request cache is enough.

The counterargument is that Redis is cheap. It can be, until it is the reason checkout times out. The decision should be based on the uncached workload, not the price of a managed add-on.

Failure Modes to Design Around

No memory ceiling. Redis grows until the host kills it. Set maxmemory and an eviction policy before the first traffic spike.

Wrong eviction policy. noeviction turns a full cache into failed writes. Use an LRU policy for a cache-only instance.

Shared keys. Two sites on one Redis instance without prefixes will read each other’s options. Use a unique prefix and, where available, a separate database.

A dead cache that hangs PHP. Long connection timeouts convert a cache outage into a site outage. Keep the timeouts short and test the failure path.

Cache stampede after a flush. Flushing a large cache during peak traffic sends every request to the database at once. Flush during low traffic, or warm the cache before restoring traffic.

Stale data after a migration. Copying a database without flushing the object cache can leave old URLs, options, and transients in Redis. Flush after the migration and before the DNS switch.

Confusing page cache with object cache. Clearing the page cache does not clear Redis, and clearing Redis does not purge a CDN. Know which layer you are troubleshooting.

The Decision Checklist

  1. Measure the page-cache hit share. If nearly every request is served from HTML, the object cache has a small audience.
  2. Measure query count and query time on the uncached templates that matter: account, cart, checkout, search, admin.
  3. Check what the host provides. A managed Redis with a memory limit and a known eviction policy is easier to operate than a self-hosted service nobody owns.
  4. Choose the simplest backend that fits. Memcached if it is already provisioned and the workload is plain key-value; Redis if you need eviction control, persistence options, or future data structures.
  5. Set the prefix, database, timeouts, and memory ceiling before enabling. These are the settings that decide whether a failure is boring.
  6. Record a baseline, enable, and compare the same templates. Use Query Monitor for the single-request view and the query logger for the weekly view.
  7. Watch hit rate, memory, and error rate for a month. A cache that is not monitored is a future incident.

If the site is also chasing Core Web Vitals, the performance-first WordPress guide covers the layer that usually moves LCP: images, fonts, and render-blocking resources. The measured speed case study shows how to attribute a real improvement instead of guessing. And if you want the server-level introduction to Redis before installing it, the Redis overview is the starting point.

FAQ

Is an object cache the same as a page cache?

No. A page cache stores rendered HTML and serves it to repeat visitors. An object cache stores database query results and other PHP objects in memory for reuse across requests. They solve different bottlenecks and can run at the same time.

Does WP_CACHE enable Redis or Memcached?

No. WP_CACHE tells WordPress to load a page-cache drop-in. It has not enabled a persistent object cache since WordPress 2.5. Check wp_using_ext_object_cache() and the presence of wp-content/object-cache.php instead.

Redis or Memcached for WordPress?

Either works for the core key-value job. Redis gives you explicit eviction policies, optional persistence, and more data types. Memcached is a simpler pure cache and is often already available. Pick the one your host can run reliably and you can monitor.

How much memory does a WordPress object cache need?

There is no universal number. Size it from the working set: the keys that are read often enough to stay resident. Start with the host’s smallest managed tier, watch used_memory and hit rate, and increase only when eviction is pushing the hit rate down.

Why is my site slow when Redis goes down?

Because PHP is waiting on a connection. Long connect and read timeouts turn a cache outage into a request outage. Set short timeouts and test the failure path before you need it. WP_REDIS_DISABLED is the emergency bypass.

Does an object cache improve Core Web Vitals?

It can improve server response time on uncached requests, which may help Time to First Byte. It will not fix an oversized hero image, a render-blocking script, or a late-loading font. Measure the metric you are trying to move before installing another layer.

Can I use an object cache on shared hosting?

Sometimes. Some shared hosts provide Redis or Memcached; many do not. If the host does not provide a persistent backend, the core object cache remains per-request, and a plugin cannot change that without a service to connect to. Ask before you plan around it.

How do I flush the object cache?

With the Redis Object Cache plugin, use wp redis flush or the admin button. With core WP-CLI, wp cache flush clears the object cache. Neither command clears your page cache or CDN, so purge those separately.

A WP Life
A WP Life

Hi! We are A WP Life, we develop best WordPress themes and plugins for blog and websites.