WordPress 7.1 + Redis Object Cache for Membership Sites
I actually maintain a WordPress site with 600+ articles and a paid members area. The moment I launched memberships I hit a weird symptom: anonymous visitors got fast pages, but the second you logged in, the dashboard and the members-only pages slowed to a crawl. The reason is structural — page caching only serves anonymous visitors, so a logged-in user carries a cookie that bypasses it, and every click lands back in PHP and MySQL to recompute the same data.
This article takes that problem apart at the layer where it actually lives. We add one layer of persistent object cache with Redis so WordPress stops rebuilding the same wp_options, transients and meta queries on every request. Everything below is based on WordPress 7.1.x, WP-CLI 2.12, Redis Object Cache 3.0 and Redis Open Source 8.10, and the commands are copy-paste ready.
📌 Disclosure: the "Programmer desk gear" section near the end contains Amazon affiliate links. If you buy through them I may earn a small commission at no extra cost to you. The technical part has no vendor relationship at all.
⏳ TL;DR (Too Long; Didn't Read)
- **Symptom**: fast for anonymous visitors, slow for logged-in users. That is the structural blind spot of page caching, not a weak server.
- **Fix**: give WordPress a layer of persistent object cache via Redis (the drop-in `wp-content/object-cache.php`), so `wp_cache_*` calls hit memory instead of being thrown away every request.
- **Prerequisites**: a Redis server (`127.0.0.1:6379` or a Unix socket) plus PHP's `redis` extension (any of PhpRedis / Relay / Predis).
- **Mandatory guardrail**: `maxmemory` + `maxmemory-policy allkeys-lru`. The default `noeviction` returns write errors once memory fills, which breaks checkout.
- **Mandatory isolation**: on a multi-site box every site needs its own `WP_CACHE_KEY_SALT` or `WP_REDIS_PREFIX`, otherwise one site reads another site's cache.
- **Acceptance test**: `wp redis status` shows Connected, and the hit rate computed from `redis-cli INFO stats` settles at 80–90%+ after warm-up.
- **Rollback**: delete `wp-content/object-cache.php`, or set `WP_REDIS_DISABLED = true` in `wp-config.php`. One second back to normal.
Why page caching cannot help logged-in users
Two things get conflated constantly:
| Cache type | What it stores | Who it mainly serves | Typical implementation |
|---|---|---|---|
| Page cache | Full HTML pages | Anonymous visitors | Nginx FastCGI Cache, CDN, page-cache plugins |
| Object cache | Database query result objects | Everyone, especially logged-in users | Redis / Memcached persistent cache taking over `wp_cache_*` |
WordPress's object cache is **non-persistent** by default: it lives only for the duration of a single request and is destroyed when that request ends. So the same piece of data gets queried 100 times across 100 requests. Membership sites, course and LMS sites, paid-content sites and stores with many signed-in customers are exactly the places where logged-in traffic dominates — page caching is nearly useless for them, while wp_options, user meta, post meta and transients are read over and over.
In one line: a membership site that is "slow the moment you log in" is not missing a page cache. It is missing a persistent object cache. That is what we are fixing here.
Prerequisites: versions, extensions and services
- WordPress: 7.1.x (the current release is 7.1.2, a security release from 2026-09-22 that fixed one critical-severity issue; 7.1.1 shipped on 2026-09-17 with 17 Core fixes, 21 Block Editor fixes and 11 security fixes). Update to 7.1.2 before you touch the cache layer.
- WP-CLI: 2.12.0, released March 2026 and the current stable version.
- Redis: Redis Open Source 8.10.x (8.10.2, 2026-09-17, is the latest; if you are still on the 8.8 line, note the CVE-2026-62356 fix that landed in 8.8.2). Valkey and other compatible servers work too.
- Object cache plugin: Redis Object Cache by Till Krüss (repo `rhubarbgroup/redis-cache`, GPL-3.0), now at 3.0.0 with 500K+ active installs on WordPress.org. The commercial Object Cache Pro 1.25.5 comes from the same team, listed from $95/month as of writing (check the official site for current pricing), and adds async writes, prefetching and cache locking.
- PHP extension: `php -m | grep redis` must list `redis` (PhpRedis) or `relay`. Watch out: the PHP that runs WP-CLI and the PHP-FPM that serves your site are often different versions — both need the extension.
Step 1: Install Redis and set a memory ceiling first
This is the step people skip, and the one that bites hardest. Redis ships with maxmemory 0 (unlimited) and maxmemory-policy noeviction — when memory fills, it does not evict anything, it simply refuses every write.
# Debian/Ubuntu
sudo apt update && sudo apt install redis-server
sudo systemctl enable --now redis-server
redis-cli ping # expect PONG
Then edit /etc/redis/redis.conf:
maxmemory 512mb
maxmemory-policy allkeys-lru
- `maxmemory`: size it against the box's total RAM. A WordPress object cache holds regenerable, disposable data, so capping it — rather than letting it fight MySQL for memory — is not optional.
- `maxmemory-policy allkeys-lru`: when the cap is hit, evict the least recently used keys. All of them can be rebuilt on demand, so this fits.
- If this Redis instance also holds data you cannot lose (queues, sessions, locks), switch to `volatile-lru` (only evict keys with a TTL), or better, give WordPress its own database or instance — otherwise LRU can throw away the data you meant to keep.
Restart afterwards: sudo systemctl restart redis-server.
> ⚠️ Never expose port 6379 to the public internet. On a single-server setup, bind 127.0.0.1. If you genuinely need cross-host access, enable requirepass or ACLs and restrict the firewall to source IPs.
Step 2: Connection constants and a key prefix in wp-config.php
Add this to wp-config.php, above the /* That's all, stop editing! */ line:
// Redis connection
define( 'WP_REDIS_HOST', '127.0.0.1' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_DATABASE', 0 );
define( 'WP_REDIS_TIMEOUT', 1 );
define( 'WP_REDIS_READ_TIMEOUT', 1 );
// Key prefix: must be unique per site, deriving from DB_NAME is bulletproof
define( 'WP_REDIS_PREFIX', DB_NAME . ':' );
define( 'WP_CACHE_KEY_SALT', DB_NAME . ':' );
Deriving the prefix from DB_NAME pays off the day you clone a site: change the database name and the cache namespace changes with it, so you never re-introduce a collision by copying wp-config.php and forgetting to edit the salt.
If your Redis only allows TLS, add define( 'WP_REDIS_SCHEME', 'tls' ); — omit it and the connection fails silently.
Step 3: Enable the drop-in and verify three layers
Installing the plugin is not what turns the cache on. The thing that actually works is the drop-in file wp-content/object-cache.php.
cd /var/www/your-site
wp plugin install redis-cache --activate
wp redis enable # creates / updates the object-cache.php drop-in
wp redis status # the key check: is Status "Connected", and which client
Verify in three layers. Miss one and you can easily end up "connected but doing nothing":
# 1. Redis is alive
redis-cli ping
# 2. WordPress thinks it is connected and the drop-in is enabled
wp redis status
# 3. Redis is actually receiving traffic
redis-cli INFO stats | grep -E "keyspace_hits|keyspace_misses"
Do step 3 as a before/after: click around the site and keyspace_hits should start climbing. "Connected" with a flat counter means the cache is not actually being used.
💣 Troubleshooting: five real errors and their fixes
Error 1: `RedisException: Connection refused` (plugin shows Not Connected)
**Cause**: Redis is not running, the host/port is wrong, PHP lacks the redis extension, or — the sneaky one — you are debugging with the CLI's PHP while the site runs a different PHP-FPM version, so the two disagree about extensions.
Diagnose and fix:
sudo systemctl status redis-server # is it active (running)
redis-cli -h 127.0.0.1 -p 6379 ping # expect PONG
php -m | grep -i redis # must also be present under the site's PHP
Install the extension for the right version (PHP 8.3 shown): sudo apt install php8.3-redis && sudo systemctl restart php8.3-fpm. And note: if you changed WP_REDIS_HOST / WP_REDIS_PORT, you changed wp-config.php, not the value already saved in the plugin UI — when they disagree, the constants win.
Error 2: `OOM command not allowed when used memory > 'maxmemory'`
**Cause**: Redis is still on the default noeviction policy and refuses writes the moment memory fills. Every post save and every WooCommerce session or transient write can trip it, and on the front end checkout or ordering starts returning errors. Redis is not broken; the policy is unset.
**Fix**: apply the maxmemory + maxmemory-policy allkeys-lru (or volatile-lru) settings from Step 1, restart, then re-check:
redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy
redis-cli INFO stats | grep evicted_keys # some eviction is normal; a runaway climb means you need more memory
Error 3: Multisite / multiple sites "cross-talking" — site A serves site B's data
**Cause**: several sites share one Redis database with no distinct key prefix. Keys like options:alloptions overwrite each other, and the symptoms look like random plugin bugs: the wrong menu, counts that do not add up, and flushing one site that also empties its neighbour.
**Fix**: give every site a unique WP_CACHE_KEY_SALT (DB_NAME . ':' is a good default), optionally plus WP_REDIS_PREFIX. On a WordPress multisite network the plugin usually folds the blog ID into the prefix automatically, but verify:
redis-cli KEYS '*' | head # you should see keys carrying the site marker / blog ID
Also remember: FLUSHDB empties the whole logical database, wiping every co-tenant site's warm cache. Prefer per-group flushes over full-database clears.
Error 4: WooCommerce cart / session weirdness (someone else's cart, stale stock)
Cause: session and cart data — which must stay dynamic — got persisted into a shared cache. That data belongs to a single request or to the database, not to a shared object cache.
Fix: exclude the non-shareable groups from the persistent cache:
define( 'WP_REDIS_IGNORED_GROUPS', [
'woocommerce_sessions',
'wc_session_id',
'cart',
'woocommerce_transients',
'session',
] );
Also: after bulk-updating stock or prices via CSV or the REST API, hooks do not always fire wp_cache_delete, so run wp cache flush when the job finishes. And under a flash sale, watch for the cache stampede — a hot key expiring while thousands of requests arrive at once sends them all to MySQL; the commercial Object Cache Pro cache lock exists precisely for that.
Error 5: You changed content, but the front end still shows the old version
Cause: two classics. First, data changed while the cache was disabled and the stale copies are still there when you re-enable (Redis Object Cache has deleted transients on enable since 2.8.0, but historical copies still need an explicit flush). Second, a plugin flushes the page cache on save but not the object cache.
Fix and diagnose:
wp cache flush # flush once before re-enabling the object cache
wp cache get alloptions options # inspect an actual key to confirm whether it is stale
Do not treat wp_cache_flush() as a cure-all — it empties the entire cache and resets the hit rate to zero while it rebuilds. Prefer per-group flushes and reserve full clears for migrations or prefix changes.
Hit rate and memory: verify with numbers, not vibes
The only basis for tuning is data. Hit rate = keyspace_hits / (keyspace_hits + keyspace_misses).
wp redis info # the plugin's aggregate view
redis-cli INFO stats | grep -E "keyspace_hits|keyspace_misses"
redis-cli INFO memory | grep -E "used_memory_human|maxmemory_human"
redis-cli SLOWLOG GET 10 # a quick look for slow commands
Rules of thumb from the community (not hard standards):
- A hit rate of **80%+** after warm-up means the layer is genuinely working; for membership and e-commerce sites even **60%+** is worthwhile.
- **Below 30% after 24 hours**: either this site does not suit an object cache, or the cache is being flushed constantly — check `evicted_keys` and hunt for plugins that flush on every save.
- Starting memory: a single site under 1,000 posts with no e-commerce usually fits in 64–128 MB; a WooCommerce store with tens of thousands of SKUs may need 256–512 MB. The steadier approach is to run a week with no cap, watch peak `used_memory`, then set `maxmemory` to 1.2–1.5× that peak.
When Redis is not worth it
Redis is not a silver bullet. These cases gain little and add a service to maintain:
- A static blog or brochure site with good page caching and almost no logged-in users — the page cache already does nearly all the work.
- A single box tight on RAM where Redis competes with MySQL and PHP — cap `maxmemory` first so it never triggers the OOM killer and takes PHP-FPM down with it.
The test is simple: the more dynamic requests and the more logged-in users, the more an object cache is worth — and the less, the less.
Programmer desk gear (with affiliate links)
This section is affiliate content, kept separate from the technical part so you can judge it for yourself.
Staring at hit-rate charts, INFO stats and slow-query logs is, in practice, hours of late-night screen time. Two things earned their place on my desk:
🖥 Dell UltraSharp U2723QE 27" 4K USB-C monitor
| Spec | Detail |
|---|---|
| Size / resolution | 27-inch / 3840×2160 |
| Ports | USB-C single-cable (with hub), DP, HDMI |
| Price (approx.) | $429–$529 as of writing; volatile, check the live page |
**Real strengths**: one USB-C cable replaces a pile of adapters; at 4K you can spread a terminal, redis-cli and log windows side by side without squinting.
Real caveat: the built-in hub delivers limited power, so a loaded desk needs external power; and the IPS panel's blacks are average in a fully dark room.
👉 Check the Dell U2723QE on Amazon >>
💡 BenQ ScreenBar Halo 2 monitor light bar
| Spec | Detail |
|---|---|
| Color temperature | 2700K–6500K stepless |
| Power | 5V/3A (a 5V/1A monitor USB port will flicker) |
| Price (approx.) | $179–$199 as of writing |
Real strengths: zero desk footprint, and late-night screen viewing stops being harsh on the eyes; the wireless dial with an LCD means no fumbling for keys in the dark.
Real caveat: in auto-dimming mode the color temperature locks at 4000K, which skews cool — you switch it manually at night; and on glossy, thick monitors the clip slides down slowly over time.
👉 Check the BenQ ScreenBar Halo 2 on Amazon >>
FAQ
Q: Do Redis object caching and page caching conflict?
No — they serve different audiences. The page cache serves anonymous visitors; the object cache serves logged-in users and dynamic requests. The best setup runs both, as long as each plugin does its own job and you do not let two "page cache" plugins overwrite the drop-in.
Q: The plugin says Connected, but my site did not get faster.
Look at the hit rate first. A flat hit rate usually means the drop-in never took over, or traffic is too low for the cache to warm up. A low-traffic site can take hours to warm; a busy one shows a difference in minutes.
Q: Free version or Object Cache Pro?
The free Redis Object Cache is enough for the vast majority of sites. Consider the commercial version only when your monthly request volume is high and you need enterprise features like async writes, prefetching and cache locking. Measure first, pay later.
Q: What is the fastest way to roll back if something breaks?
Delete wp-content/object-cache.php (equivalent to switching the object cache off), or temporarily add define( 'WP_REDIS_DISABLED', true ); to wp-config.php. A live site should always have a one-second escape hatch.
Wrapping up
"Slow the moment you log in" on a membership site is really a missing persistent object cache, not a weak server. Once Redis is in place, three things decide whether it works: the maxmemory and eviction policy, key isolation across sites, and verifying with a hit rate instead of a feeling.
If what you actually care about is the database itself — indexes, `wp_postmeta` and `meta_query` slow queries — start with my earlier piece, How I Cut a WordPress Query from 3.2s to 180ms. Object caching and indexing are two different paths: fix slow queries first, then layer the cache, and keep that order. If order notification emails are still your headache, WooCommerce 11.1 Order Emails Not Arriving pairs well with the "session bleed" section above, and if you maintain hundreds of articles, WordPress 7.1 + WP-CLI: Rebuilding Internal Links at Scale is the companion read.
👉 Join MiniMax Token Plan: AI coding acceleration for businesses
👉 Join Xiaomi MiMo Platform: Leading AI model platform with cost-effective inference
👉 Join Aliyun AI: Top AI products with exclusive coupons for business innovation
📌 This article was AI-assisted generated and human-reviewed | TechPassive — An AI-driven content testing site focused on real tool reviews
🔗 Recommended Tools
These are carefully selected tools. Using our affiliate links supports us to keep producing quality content: