"WHMCS is slow" is solved by two things: OPcache and Redis. Together they cut typical page generation time by 4-6x on a busy install. Neither requires code changes. Neither requires a marketplace module. Both take 30 minutes to set up. Yet most WHMCS installs ship without either.
This is the working setup.
What OPcache and Redis actually do
OPcache is a PHP extension that caches compiled PHP bytecode in shared memory. Without it, every page request reparses every .php file from disk. WHMCS has hundreds of files; the savings are massive. OPcache is non-optional in production.
Redis is an in-memory data store. For WHMCS, it serves three purposes:
- Session storage — faster than the default file-based PHP sessions, plus survives traffic spikes.
- Application cache — many WHMCS-aware caching modules (and your own custom hooks) can store derived data here.
- Rate limiting + queues — if you're building advanced integrations.
OPcache caches code. Redis caches data. They're complementary; you want both.
Part 1 — OPcache setup (15 minutes)
OPcache ships with PHP. You just enable and tune it.
Edit your php.ini (or the OPcache-specific config file — varies by distro, often /etc/php.d/10-opcache.ini):
; Enable OPcache
opcache.enable = 1
opcache.enable_cli = 0 ; don't enable for CLI — saves RAM, no benefit for cron
; Memory
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 16
; File count
opcache.max_accelerated_files = 20000 ; WHMCS + custom modules easily exceed default 4000
; Validation strategy
opcache.revalidate_freq = 60 ; check if files changed every 60 sec
opcache.validate_timestamps = 1 ; set to 0 only if your deploy clears OPcache explicitly
; Performance
opcache.fast_shutdown = 1
opcache.enable_file_override = 1
opcache.huge_code_pages = 0 ; usually 0 unless you've configured huge pages
Restart PHP-FPM (or Apache):
systemctl restart php-fpm
# or for cPanel
/usr/local/cpanel/scripts/restartsrv_apache_php_fpm
Verify:
php -i | grep opcache.enable
# Should show: opcache.enable => On
For more detail, install opcache-gui — a single-file PHP app that shows hit rate, memory usage, and cached file count. Aim for hit rate >95% under load.
Part 2 — Redis setup (15 minutes)
Install Redis
# RHEL / Rocky / AlmaLinux
yum install epel-release
yum install redis
systemctl enable --now redis
# Debian / Ubuntu
apt update
apt install redis-server
systemctl enable --now redis-server
# Verify
redis-cli ping
# Should respond: PONG
Configure Redis for WHMCS
Edit /etc/redis.conf (or /etc/redis/redis.conf on Debian/Ubuntu):
# Memory — size depends on your WHMCS scale
maxmemory 512mb
# Eviction policy — least-recently-used is the right choice for caching
maxmemory-policy allkeys-lru
# Persistence — for cache-only, disable to save IO
save ""
appendonly no
# Bind only to localhost unless you're running WHMCS on a different server
bind 127.0.0.1
protected-mode yes
# If WHMCS is on a different server, bind to the private IP and require a password
# bind 10.0.0.1
# requirepass strong-random-password-here
Restart Redis:
systemctl restart redis
Install the PHP Redis extension
# RHEL / Rocky / AlmaLinux
yum install php-pecl-redis
# Debian / Ubuntu
apt install php-redis
# Or via PECL for custom PHP builds
pecl install redis
echo "extension=redis.so" > /etc/php.d/40-redis.ini
# Verify
php -m | grep redis
Restart PHP-FPM after installing the extension.
Point WHMCS sessions to Redis
Edit your php.ini:
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379"
; If you set a Redis password:
; session.save_path = "tcp://127.0.0.1:6379?auth=YOUR_PASSWORD"
Restart PHP-FPM. WHMCS now stores sessions in Redis instead of disk. You should see immediate improvement on session-heavy pages (admin panel, client area).
Caching strategy — what to cache in your custom code
Once Redis is available, your custom hooks and modules can use it directly. The pattern I use:
function getCachedClientLoyaltyTier(int $clientId): string
{
static $redis = null;
if ($redis === null) {
$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
}
$cacheKey = "client:loyalty:{$clientId}";
$cached = $redis->get($cacheKey);
if ($cached !== false) {
return $cached;
}
// Expensive calculation
$monthsActive = Capsule::table('tblclients')
->where('id', $clientId)
->value(Capsule::raw('TIMESTAMPDIFF(MONTH, datecreated, NOW())'));
$tier = match (true) {
$monthsActive >= 60 => 'Diamond',
$monthsActive >= 36 => 'Platinum',
$monthsActive >= 12 => 'Gold',
default => 'Silver',
};
$redis->setex($cacheKey, 3600, $tier); // expire in 1 hour
return $tier;
}
Used inside the ClientAreaPage hook (which fires on every client page load), this cuts the per-page DB query overhead substantially.
What NOT to cache
Some data must never be cached:
- Financial state (invoices owed, account balance, ticket status).
- Anything used for an authorization decision (permissions, role, suspended status).
- Anything that changes in response to user actions on the same page.
Cache derived, slow-to-compute, non-authoritative data: loyalty tiers, recent activity summaries, aggregated stats. Authoritative data stays in MySQL.
How to verify everything is working
OPcache
php -r 'print_r(opcache_get_status());'
# Look at: hit_rate, num_cached_files, used_memory
After WHMCS pages have been hit a few times: hit rate >90%, used memory under your limit, no "out of memory" entries.
Redis
redis-cli info stats
# total_connections_received: should grow steadily
# keyspace_hits / keyspace_misses: ratio should improve as cache warms
redis-cli info memory
# used_memory_human: confirm it's growing but under your maxmemory
redis-cli --stat
# Live view of operations per second
WHMCS sessions in Redis
redis-cli keys "PHPREDIS_SESSION:*" | wc -l
# Should be roughly equal to concurrent WHMCS users
Page-load improvement
Measure before and after. WHMCS admin pages: open browser DevTools → Network → reload your dashboard. Compare "Server response time" (TTFB).
Typical improvement on a moderately busy install: 600-900ms → 150-300ms. Bigger improvements on slower hardware.
Going further — full WHMCS object caching
WHMCS doesn't ship native Redis object caching, but you can build it via hooks. The pattern:
- Use
ClientAreaPagefilter hook to intercept expensive queries. - Check Redis first; query DB only on miss.
- Use
InvoicePaid,ClientEdit, etc. action hooks to invalidate relevant cache keys when underlying data changes.
This is a meaningful engineering effort but pays off on installs with >10k customers. Below that, the OPcache + Redis-sessions setup above is enough.
Common pitfalls
"Redis is installed but sessions still feel slow." PHP didn't pick up the session config change. Check php -i | grep session.save_handler — should say redis. If it says files, your php.ini edit didn't apply (wrong file, didn't restart PHP-FPM).
"OPcache is enabled but hit rate is low (<50%)." opcache.max_accelerated_files is too low and OPcache is evicting recently-used files. Raise it to 20000.
"Random 'session not found' errors after enabling Redis." Redis ran out of memory and evicted active sessions. Increase maxmemory or change maxmemory-policy to volatile-lru (only evicts items with TTL set, which sessions have).
"Code changes don't appear after deploy." OPcache is caching the old version. Either:
- Set
opcache.revalidate_freq = 0in dev (revalidate on every request — slower but accurate). - In production with
validate_timestamps = 0, restart PHP-FPM after deploy, or callopcache_reset()programmatically.
"Redis is being accessed remotely without auth." Critical security issue. Bind only to localhost, or set a password + use TLS. Redis defaults are open by design for ease of dev; for production lock it down.
My take — order of operations
- Enable OPcache first. Free. Massive impact. Every install should have it.
- Add Redis once you exceed ~500 customers — meaningful gain for session-heavy admin areas.
- Skip "WHMCS speed-up" marketplace modules until you've done the above. Most of them are wrappers around what OPcache and Redis do for free.
- Tune MySQL aggressively too — see my system requirements guide.
Going further
I tune WHMCS deployments for performance — OPcache + Redis, MySQL config, slow-query analysis, caching strategy in custom code. If your WHMCS feels sluggish, tell me about your setup and I'll send a quote in 24 hours.