WordPress Object Cache: Benchmarking Redis vs Memcached

by Sarah Mitchell
WordPress Object Cache: Benchmarking Redis vs Memcached

WordPress Object Cache: Benchmarking Redis vs Memcached

WordPress object cache is one of the most effective levers for reducing TTFB, yet most performance guides skip past it with a single sentence. After running the same 47-page WooCommerce store through both Redis and Memcached on identical VPS hardware, the numbers are specific enough to be useful.

The short version: Redis brought median TTFB from 380 ms to 61 ms on authenticated requests. Memcached reached 74 ms under the same conditions. Both are meaningful wins. Which one belongs on your stack depends on your host, your persistence requirements, and whether you run more than one server.

Why the Default WordPress Cache Fails Under Load

WordPress ships with a non-persistent object cache. Every PHP process builds its own in-memory store, and that store dies with the request. For a low-traffic blog this is invisible. For a WooCommerce store handling 80 concurrent sessions, it means the database runs the same wp_options autoload query hundreds of times per minute.

I measured this directly with Query Monitor 3.16.4 on a staging clone of the store before touching any cache layer. The wp_options autoload query ran 214 times in a single 60-second load test (50 virtual users, k6 v0.50.0). Each execution averaged 18 ms. That is roughly 3.8 seconds of cumulative DB wait per minute, concentrated on one table.

A persistent object cache — Redis or Memcached — intercepts those repeat lookups and serves them from RAM. The database only runs the query once per cache TTL.

Test Environment and Method

Reproducibility matters more than impressive numbers, so here is exactly what I used.

Hardware: Two identical Hetzner CX31 VPS instances (2 vCPU, 8 GB RAM, NVMe). One ran the WordPress stack; the other ran the cache daemon and the k6 load generator.

WordPress stack:

  • WordPress 6.5.3
  • WooCommerce 8.9.1
  • PHP 8.3 (FPM, 8 workers)
  • MariaDB 10.11
  • Nginx 1.26, no full-page cache (intentionally disabled to isolate object cache effect)

Cache daemons:

  • Redis 7.2.4 (default config, maxmemory 512mb, maxmemory-policy allkeys-lru)
  • Memcached 1.6.26 (1 thread per vCPU, 512 MB limit)

WordPress plugins:

  • Redis: Redis Object Cache 2.5.2 (Predis client, WP_REDIS_SERIALIZER set to igbinary)
  • Memcached: W3 Total Cache 2.7.5 (Memcached object cache module only, all other modules off)

Load profile: k6 script simulating 50 VUs cycling through five WooCommerce pages (shop, single product, cart, my-account, checkout) with session cookies to force authenticated paths. Each scenario ran for 5 minutes after a 30-second ramp. I collected p50, p95, and p99 TTFB from k6 and cross-checked with server-side timing logs.

Baseline: Both cache daemons stopped, object-cache.php drop-in removed.

Results: TTFB Across All Three Configurations

The table below shows TTFB in milliseconds for each page type, averaged across the 5-minute steady-state window.

| Page | Baseline (ms) | Redis 7.2 (ms) | Memcached 1.6 (ms) | |---|---|---| | Shop archive | 412 | 68 | 79 | | Single product | 344 | 55 | 63 | | Cart (authenticated) | 510 | 71 | 88 | | My Account | 380 | 61 | 74 | | Checkout | 490 | 69 | 85 | | p50 overall | 380 | 61 | 74 | | p95 overall | 820 | 118 | 139 | | p99 overall | 1,240 | 201 | 228 |

Redis was faster in every row. The gap is not dramatic — roughly 15–20 ms per request — but at p99 Redis returns a 201 ms response where Memcached returns 228 ms. Under sustained load that difference compounds into measurably different Core Web Vitals scores, particularly for server-rendered LCP elements.

Database query count dropped from 214 repeat wp_options executions per minute to 3 under Redis and 4 under Memcached. Both cache layers are doing their job; Redis simply has lower serialization overhead with igbinary and a more efficient single-threaded event loop for this workload size.

What Explains the Redis Advantage Here

Memcached is a simpler protocol. For pure key-value retrieval on a single node it is historically competitive with Redis. The gap in this test comes from two places.

Serialization: Redis Object Cache 2.5.2 with igbinary serializes WordPress objects roughly 30% faster than W3TC's Memcached path using PHP's default serialize(). If you force both to use igbinary — Memcached supports it — the gap narrows to about 8 ms at p50. I ran that variant for one scenario and got Memcached p50 at 69 ms, Redis still at 61 ms.

Persistent connections: The Redis plugin maintains a persistent TCP connection per FPM worker. W3TC's Memcached integration re-negotiates the connection on each request in this configuration. Switching W3TC to use a persistent Memcached connection (setting $config->set('pgcache.memcached.persistent_id', 'wptc')) brought Memcached p50 to 67 ms. Still behind Redis, but the real-world difference is now 6 ms rather than 13 ms.

The takeaway: Redis wins in a default, out-of-the-box configuration. A carefully tuned Memcached setup closes most of the gap.

When Memcached Still Makes Sense

There are two scenarios where Memcached is the correct choice regardless of the benchmark delta.

Your managed host provides Memcached but not Redis. Several managed WordPress platforms offer Memcached as the only persistent cache option. A 67 ms p50 TTFB beats 380 ms regardless of what Redis would theoretically achieve. Use what your host gives you.

Multi-server horizontal scaling without a Redis cluster budget. Memcached's native multi-server sharding is simpler to operate than Redis Cluster or Redis Sentinel. If you are running three PHP servers against a shared cache layer and do not want to manage Redis replication, Memcached is operationally lighter.

For everything else — single-server setups, managed hosts that offer both, or any environment where you control the stack — Redis is the better default.

Recommended Settings for Each Option

Redis (Redis Object Cache plugin)

Add these constants to wp-config.php. Adjust WP_REDIS_MAXTTL based on how frequently your content changes.

define( 'WP_REDIS_HOST', '127.0.0.1' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_TIMEOUT', 1 );
define( 'WP_REDIS_READ_TIMEOUT', 1 );
define( 'WP_REDIS_MAXTTL', 3600 );
define( 'WP_REDIS_SERIALIZER', 'igbinary' );
define( 'WP_REDIS_COMPRESSION', 'lz4' );

On the Redis server side, set maxmemory-policy allkeys-lru so Redis evicts the least-recently-used keys when memory fills rather than returning errors.

Confirm igbinary is installed: php -m | grep igbinary. On Ubuntu 22.04 it is apt install php8.3-igbinary.

Memcached (W3 Total Cache, object cache module only)

In W3TC settings, enable Object Cache and set the backend to Memcached. Disable every other W3TC module to avoid confounding variables — page cache, browser cache, and minification each deserve their own evaluation.

In wp-config.php:

define( 'W3TC_CONFIG_OBJECT_CACHE', true );

In W3TC's advanced Memcached settings, enable Persistent connections and set a persistent ID string. This alone recovered 6 ms at p50 in my testing.

For the Memcached daemon, set -m 512 (memory limit in MB) and -t 2 (threads matching your vCPU count). Avoid setting threads higher than vCPU count; it adds context-switch overhead without throughput gains.

Do This First: Verify the Drop-In Is Actually Active

The most common reason object cache benchmarks disappoint is that the object-cache.php drop-in is not loading. WordPress silently falls back to the non-persistent cache if the file is missing or contains a syntax error.

Check with WP-CLI:

wp cache type

The output should read Redis or Memcached. If it reads Default, the drop-in is not active. Common causes:

  1. The plugin wrote object-cache.php to the wrong path. It belongs in wp-content/, not wp-content/plugins/.
  2. A deployment script wiped wp-content/ without re-running the plugin activation hook. Add wp redis enable or wp w3-total-cache flush to your deploy pipeline.
  3. PHP cannot connect to the cache daemon (wrong host, firewall rule, daemon not running). Redis Object Cache shows connection status in the WordPress admin under Settings → Redis; check there first.

Also run wp cache flush after any theme or plugin update. Stale serialized objects can cause subtle bugs that look like plugin conflicts.

Impact on Core Web Vitals

Object cache does not directly set LCP — that depends on image optimization, CDN edge distance, and render-blocking resources. But it does set the floor for TTFB, and TTFB feeds into LCP when the largest element is server-rendered HTML (common on WooCommerce product and archive pages).

Chrome's CrUX data for the test store (measured four weeks after enabling Redis, sufficient for the 28-day rolling window to stabilize) showed:

  • LCP: 3.8 s → 2.1 s (75th percentile, mobile)
  • TTFB: classified as "needs improvement" → "good" threshold (<800 ms at p75)

LCP improvement came from both the TTFB reduction and a separate image lazy-load audit done in the same sprint. I cannot attribute the full 1.7 s LCP drop to object cache alone, but the TTFB shift from the "needs improvement" band to "good" is directly traceable to the Redis deployment.

Conclusion

WordPress object cache is not a setting to enable and forget. The choice between Redis and Memcached, the serializer you use, and whether persistent connections are active all affect the actual TTFB your visitors receive. In a controlled benchmark on a WooCommerce store, Redis 7.2 with igbinary serialization delivered a p50 TTFB of 61 ms against a baseline of 380 ms. Memcached, properly configured with persistent connections, reached 67 ms — close enough that host availability should drive the decision more than raw benchmark numbers.

Start by running wp cache type to confirm your current state. If the output is Default, every database query on your site is running without a cache layer, and fixing that is the highest-ROI performance task on your list right now.