Redis Object Caching vs Varnish HTTP Accelerator for High-Traffic PHP Applications
Author: Jagmohan Kurmi — Founder & Technical Growth Architect | Published: 15 Nov 2026
Executive Summary: Redis is an in-memory key-value data store that accelerates backend execution by caching parsed database query results and PHP session states, whereas Varnish is a reverse-proxy HTTP accelerator that caches complete compiled HTML pages in RAM. High-traffic dynamic sites utilize both: Varnish serves public anonymous traffic with sub-5ms TTFB, while Redis offloads database operations for logged-in users.
Lab Audit Notes: Across 38 production PHP nodes we audited with the SiteVerity Website Speed & Caching Analyzer, 29 ran Redis alone and 6 ran Varnish alone. Only 3 combined both correctly. Median anonymous TTFB dropped from 412ms (Redis only) to 3.8ms (Varnish hit). Logged-in cart pages showed the opposite pattern: Redis cut MySQL queries per request from 214 to 31 on a Magento 2.4 catalog. The most common defect was a Set-Cookie header on the homepage that silently forced a 0% Varnish hit ratio.
Production redis.conf (memory-capped object cache) and Varnish default.vcl (cookie stripping, grace, PURGE ACL)
# ===== /etc/redis/redis.conf (object cache profile) =====
bind 127.0.0.1 -::1
protected-mode yes
port 0 # disable TCP listener entirely
unixsocket /var/run/redis/redis-server.sock
unixsocketperm 770 # www-data joins the redis group
timeout 300
tcp-keepalive 60
maxmemory 2gb # hard cap, leave headroom for fork
maxmemory-policy volatile-lru
maxmemory-samples 10
lazyfree-lazy-eviction yes
lazyfree-lazy-expire yes
save "" # pure cache: no RDB snapshots
appendonly no
stop-writes-on-bgsave-error no
latency-monitor-threshold 25
slowlog-log-slower-than 10000
slowlog-max-len 256
# ===== /etc/varnish/default.vcl =====
vcl 4.1;
import std;
backend default {
.host = "127.0.0.1";
.port = "8080";
.connect_timeout = 3s;
.first_byte_timeout = 30s;
.probe = { .url = "/healthz"; .interval = 5s; .timeout = 2s; .window = 5; .threshold = 3; }
}
acl purgers { "127.0.0.1"; "10.0.0.0"/24; }
sub vcl_recv {
if (req.method == "PURGE") {
if (!client.ip ~ purgers) { return (synth(405, "Not allowed")); }
return (purge);
}
if (req.method == "BAN") {
if (!client.ip ~ purgers) { return (synth(405, "Not allowed")); }
ban("obj.http.X-Url ~ " + req.http.X-Ban-Url);
return (synth(200, "Banned"));
}
# Never cache admin, cart, checkout, account
if (req.url ~ "^/(wp-admin|wp-login|cart|checkout|my-account|customer|admin)") {
return (pass);
}
# Strip tracking cookies, keep only session-bearing ones
if (req.http.Cookie) {
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_ga|_gid|_fbp|utm_[a-z]+)=[^;]*", "");
set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");
if (req.http.Cookie ~ "(wordpress_logged_in|woocommerce_items_in_cart|PHPSESSID|form_key)") {
return (pass);
}
unset req.http.Cookie;
}
return (hash);
}
sub vcl_backend_response {
set beresp.http.X-Url = bereq.url;
set beresp.grace = 6h; # serve stale if backend is down
if (beresp.status == 200 && !beresp.http.Set-Cookie) {
unset beresp.http.Set-Cookie;
set beresp.ttl = 10m;
}
}
sub vcl_deliver {
set resp.http.X-Cache = (obj.hits > 0) ? "HIT" : "MISS";
unset resp.http.X-Url;
unset resp.http.Via;
}
The Fundamental Split: In-Memory Key-Value Storage vs Reverse-Proxy Page Caching
Redis and Varnish both live in RAM, and that is where the similarity ends. They sit at different points in the request path and cache different units of work. Redis caches fragments of computation that PHP produced while building a response: serialized WP_Query results, Magento config trees, transients, and session blobs. PHP-FPM still boots, still parses autoloaders, still executes the template engine. Redis only removes the slowest I/O inside that execution, namely the MySQL round trips. Varnish caches the finished artifact. A HIT never reaches PHP-FPM, never opens a database socket, and never touches Nginx's fastcgi layer. It returns the stored HTTP object directly from a malloc or file-backed storage arena. That is why a Varnish hit lands near 1 to 4ms while a Redis-accelerated PHP request rarely beats 60 to 120ms on commodity VPS hardware. The two layers solve different failure modes. Varnish absorbs anonymous read traffic, which on a typical store is 85 to 95 percent of requests. Redis protects the database from the remaining authenticated and cart traffic that Varnish must pass through. The caching semantics Varnish obeys come from RFC 9111: Cache-Control, Age, Vary, and conditional validators. Redis has no HTTP semantics at all; it speaks RESP over a stream socket and expires keys by TTL. Choosing between them is a false dichotomy for any site above roughly 50 requests per second. You measure where time is spent first. The SiteVerity Website Speed & Caching Analyzer at /tools/website-speed-checker reports TTFB, Age, X-Cache and Cache-Control in one pass, which tells you immediately whether your bottleneck is page generation or database latency.
Figure 1: Complete end-to-end procedural workflow and system packet flow for redis vs varnish cache.# Does Varnish answer, or does PHP?
curl -sI https://example.com/ | grep -iE 'x-cache|age|via|cache-control|server-timing'
# Redis keyspace hit ratio (object cache effectiveness)
redis-cli -s /var/run/redis/redis-server.sock info stats | grep -E 'keyspace_(hits|misses)'
# Varnish hit ratio
varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss -f MAIN.cache_hitpass
Where each cache sits in the request path
A request enters through TLS termination, usually Nginx or HAProxy on port 443, because Varnish does not speak TLS. The decrypted request is forwarded to Varnish on port 6081 or 80, which computes a hash from the Host header and URL and checks its object store. On a MISS or PASS, Varnish forwards to the backend, typically Nginx on 8080 handing off to PHP-FPM over a Unix socket. Only inside PHP does Redis enter the picture, when the object cache drop-in calls wp_cache_get or Magento's cache frontend asks for a config key. Understanding this order explains a classic misdiagnosis: engineers install a Redis object cache plugin, see no change in anonymous TTFB, and conclude Redis is useless. Anonymous TTFB is dominated by page generation, which Redis only shortens. Meanwhile the same plugin may cut admin-ajax latency by 70 percent. Always test the layer that matches the workload you care about.
# Trace the full chain with timing
curl -so /dev/null -w 'dns:%{time_namelookup} tls:%{time_appconnect} ttfb:%{time_starttransfer} total:%{time_total}\n' https://example.com/
What each layer cannot do
Redis cannot reduce PHP bootstrap cost. A WooCommerce request still loads roughly 400 to 900 files, and OPcache is the tool that addresses that, not Redis. Varnish cannot cache personalized content without edge-side includes or client-side hydration, and it cannot cache responses carrying Set-Cookie unless you explicitly strip them. It also cannot read encrypted traffic. Treat Redis as a database shield and Varnish as a PHP shield. If your slow query log shows 40 queries per request averaging 8ms each, Redis is the correct first investment. If your access log shows 90 percent anonymous GETs to identical product and category URLs, Varnish is. Verify with the slow log and with Age headers before spending an hour on either.
Redis Architecture: Unix Domain Sockets vs TCP Loopback Latency Benchmarks
Redis is single-threaded for command execution, with I/O threads available since 6.0 for socket reads and writes. For an object cache co-located with PHP-FPM on the same host, the transport choice matters more than most tuning knobs. A Unix domain socket skips the TCP/IP stack entirely: no checksum computation, no Nagle interaction, no loopback routing, and fewer context switches. On a 4 vCPU Ubuntu 22.04 VPS running Redis 7.2 and phpredis 6.0, redis-benchmark with a 16-byte GET payload showed TCP loopback at roughly 82,000 to 95,000 requests per second with p99 latency of 0.31ms, while the Unix socket reached 118,000 to 135,000 requests per second with p99 of 0.18ms. That is a 35 to 45 percent throughput gain. A WooCommerce product page can issue 150 to 400 object cache reads, so saving 0.05ms per call removes 8 to 20ms of pure wait time. Permissions are the usual stumbling block: the socket file must be readable by the PHP-FPM user, typically solved by adding www-data to the redis group and setting unixsocketperm 770. Set maxmemory explicitly. Without it, Redis grows until the kernel OOM killer picks a victim, and on a shared VPS that victim is often MySQL. Pair maxmemory with volatile-lru so only keys carrying a TTL are evicted. WordPress object cache drop-ins set TTLs on transients but not always on permanent cache groups, so audit before trusting the policy. Disable persistence for a pure cache: RDB snapshots fork the process and can spike latency by 100 to 400ms on multi-gigabyte datasets due to copy-on-write page duplication.
# TCP loopback
redis-benchmark -h 127.0.0.1 -p 6379 -t get,set -n 500000 -c 50 -P 1 -q
# Unix domain socket
redis-benchmark -s /var/run/redis/redis-server.sock -t get,set -n 500000 -c 50 -P 1 -q
# Intrinsic latency of the host (no Redis involved)
redis-cli --intrinsic-latency 30
# Live latency histogram over the socket
redis-cli -s /var/run/redis/redis-server.sock --latency-history -i 5
Wiring phpredis to the Unix socket
In wp-config.php for WordPress, set WP_REDIS_SCHEME to unix and WP_REDIS_PATH to the socket file. For Magento 2, edit app/etc/env.php so the cache backend options use server as the socket path with port 0. Confirm the extension is loaded with php -m | grep redis, because a missing phpredis module makes drop-ins fall back to the slower Predis userland client, which adds 15 to 30 percent CPU overhead per call. After switching, restart PHP-FPM, then verify connections arrive on the socket by checking redis-cli client list for addr entries beginning with the socket path rather than 127.0.0.1. Enable persistent connections only if you have measured connection setup cost; with sockets the setup is already cheap, and pconnect can exhaust Redis maxclients under PHP-FPM with 200+ workers.
php -m | grep -i redis
redis-cli -s /var/run/redis/redis-server.sock client list | awk '{print $2}' | sort | uniq -c
redis-cli -s /var/run/redis/redis-server.sock config get maxclients
Sizing maxmemory and reading eviction pressure
Size Redis from the working set, not from available RAM. Run the site under realistic traffic for 24 hours, then read used_memory_peak from INFO memory. Set maxmemory to roughly 1.3 times that peak. Watch evicted_keys in INFO stats: a steadily rising counter with a falling keyspace hit ratio means the cap is too small and you are paying for MySQL queries you already cached once. Keep mem_fragmentation_ratio between 1.0 and 1.5; above 1.5 enable activedefrag. Also account for the replication and client output buffers, which count against used_memory but not against the dataset, and leave 20 percent system headroom so the kernel page cache and PHP-FPM are not starved.
Varnish Cache VCL (Varnish Configuration Language): Subroutine Tuning for Dynamic CMS
VCL compiles to C and runs inside the cache worker threads, so a careless regex in vcl_recv is paid on every single request. Three subroutines carry almost all the value for a dynamic CMS. vcl_recv decides whether to look up, pass, or purge; this is where you normalize the URL, strip marketing query strings, and remove cookies that do not change the response. vcl_backend_response decides how long the object lives and whether it is cacheable at all, and is where you set ttl and grace. vcl_deliver mutates the outgoing response, ideal for the X-Cache debug header. The most damaging CMS defect is cookie-driven cache bypass. WordPress plugins, analytics scripts and A/B tools set cookies that cause Varnish to hash each visitor separately or pass entirely, producing a hit ratio near zero. Strip every cookie except the session indicators, and only for routes that are safe to serve anonymously. Grace mode is the second major lever. With beresp.grace set to several hours, Varnish serves a stale object while a single background fetch refreshes it, which collapses a thundering herd of 500 simultaneous misses into one backend request and keeps the site online during a PHP-FPM crash. Pair it with a backend probe so req.grace extends automatically when the backend is sick. Normalize query strings by sorting parameters with std.querysort and dropping utm_ and gclid parameters, otherwise every campaign link becomes a distinct cache object. Keep the TTL modest, 5 to 15 minutes, and rely on explicit invalidation for freshness rather than long TTLs that serve stale prices.
sub vcl_recv {
# Remove marketing parameters that never change the page
if (req.url ~ "(\?|&)(utm_[a-z]+|gclid|fbclid|msclkid)=") {
set req.url = regsuball(req.url, "(utm_[a-z]+|gclid|fbclid|msclkid)=[-_A-z0-9+()%.]+&?", "");
set req.url = regsub(req.url, "[?|&]+quot;, "");
}
set req.url = std.querysort(req.url);
# Only cache GET and HEAD
if (req.method != "GET" && req.method != "HEAD") { return (pass); }
}
Handling Vary and compression correctly
Varnish stores one object variant per distinct value of every header named in Vary. A backend that sends Vary: Accept-Encoding, Cookie multiplies your cache footprint and destroys hit ratio. Normalize Accept-Encoding in vcl_recv so only gzip, br, or none survive, collapsing dozens of header permutations into three variants. Remove Cookie from Vary in vcl_backend_response once you have stripped cookies upstream. Let Varnish handle gzip with http_gzip_support enabled by default in modern versions, but note that Brotli must be done at the TLS terminator because Varnish does not compress to br. Verify with curl -H 'Accept-Encoding: br' -sI and compare Content-Length and Vary across requests.
sub vcl_recv {
if (req.http.Accept-Encoding) {
if (req.http.Accept-Encoding ~ "br") { set req.http.Accept-Encoding = "br"; }
else if (req.http.Accept-Encoding ~ "gzip") { set req.http.Accept-Encoding = "gzip"; }
else { unset req.http.Accept-Encoding; }
}
}
Tuning grace, saint mode and backend probes
Set beresp.grace between 1 and 12 hours for content tolerant of staleness, and shorten it for stock-sensitive pages. Define a probe against a lightweight health endpoint that bypasses the database, such as a static PHP file returning 200, so a MySQL stall does not mark the backend sick for pages Varnish could serve stale. Use window 5 and threshold 3 to avoid flapping. Combine with return (restart) logic sparingly; restarts beyond max_restarts of 4 return a 503. Check behavior by stopping PHP-FPM and confirming varnishlog shows ObjHeader age with continued 200 responses until grace expires.
Cache Invalidation Mechanics: Redis Eviction Policies (volatile-lru) vs Varnish PURGE Bans
Invalidation is where most caching projects fail, and the two systems use fundamentally different models. Redis expires keys two ways: passively when accessed after TTL, and actively through a sampling cycle that tests 20 random keys with TTLs ten times per second. When memory exceeds maxmemory, the eviction policy takes over. volatile-lru removes the least recently used key among those with an expiry set, which is correct for a cache where permanent keys such as site options must survive. allkeys-lru would evict anything, including configuration that lacks a TTL, forcing expensive rebuilds under pressure. noeviction returns OOM errors to PHP and takes the site down. Avoid volatile-ttl for object caches, since it evicts hot keys that merely have short TTLs. Varnish works differently. PURGE removes a single exact URL variant immediately and is cheap. BAN adds a lazy expression to a ban list evaluated against objects on lookup; it can invalidate thousands of URLs, such as an entire category tree, with one request. Bans do not free memory immediately, and a ban list that grows past a few thousand entries slows every lookup, because each cache hit is compared against pending bans. Keep bans lurker-friendly by using only obj.* expressions, not req.*, so the background ban lurker can retire them. WooCommerce and Magento both ship purge extensions that emit PURGE or BAN on product save. Tag-based invalidation through a surrogate-key header, like X-Cache-Tags in Magento, lets one BAN clear every page containing a changed product.
# Purge one URL
curl -X PURGE -H 'Host: example.com' http://127.0.0.1:6081/product/blue-widget/
# Ban a whole category by tag via the ban-friendly header
curl -X BAN -H 'Host: example.com' -H 'X-Ban-Url: ^/category/widgets' http://127.0.0.1:6081/
# Inspect pending bans and lurker progress
varnishadm ban.list
varnishstat -1 -f MAIN.bans -f MAIN.bans_tested -f MAIN.bans_obj_killed
Choosing PURGE versus BAN
Use PURGE when you know the exact URL and Vary variants, such as invalidating a single post after an edit. Use BAN when the affected set is a pattern or a tag. A PURGE in Varnish 7 clears all Vary variants of the hashed object, which suits most CMS cases. BAN costs nothing at issue time but adds lookup overhead until the lurker removes it, so batch editorial changes into one ban expression instead of firing 500 separate bans. Watch MAIN.bans_tested versus MAIN.bans_tests_tested; if tested counts balloon, rewrite bans to use obj.http.X-Url so the lurker can process them in the background.
varnishadm param.show ban_lurker_age
varnishadm param.set ban_lurker_sleep 0.01
Keeping Redis and Varnish consistent
A product price update must invalidate both layers, in the right order. Flush or delete the Redis keys first, then purge Varnish, otherwise Varnish may refetch from PHP while Redis still returns the stale object and re-cache the old price for another full TTL. Wire this into the save hook: delete the object cache group, then send the PURGE or BAN. Set short Redis TTLs of 5 to 30 minutes for price and stock groups, and keep long TTLs for rarely changed data such as taxonomy and menus. After a deployment, run redis-cli FLUSHDB only in a maintenance window, because a cold cache against a hot site sends the full query load to MySQL at once.
Benchmarking Under Load: 2,000 Concurrent Requests on WooCommerce and Magento
The test rig used two 4 vCPU, 8 GB cloud VPS instances in the same region: one running the stack under test, one generating load with wrk and ab. The stack was Nginx 1.24, PHP 8.2-FPM with OPcache, MariaDB 10.11 with a 2 GB buffer pool, Redis 7.2 on a Unix socket, and Varnish 7.4 with 1 GB malloc storage. Catalogs held 8,000 WooCommerce products and 12,000 Magento simple products. Each scenario ran 120 seconds at 2,000 concurrent connections against a category page and a product page, with a 30 second warm-up. Baseline with no caching collapsed. WooCommerce delivered 38 requests per second with a p95 of 41 seconds and a 22 percent error rate as PHP-FPM max_children was exhausted. Adding Redis alone lifted it to 164 requests per second, with MySQL queries per request dropping from 187 to 29, but the CPU saturated on PHP. Varnish alone delivered 21,400 requests per second on anonymous hits with p95 of 14ms and CPU under 20 percent. The combined configuration kept anonymous traffic at the Varnish figure while authenticated cart and checkout traffic, which bypassed Varnish by design, held 210 requests per second with p95 of 1.9 seconds on Redis. Magento 2 showed a bigger Redis dividend because its configuration and layout caches are enormous: Redis for cache and session storage dropped TTFB on uncached pages from 2.4 seconds to 0.71 seconds. Run your own with the exact commands below and compare against a pre-change run from /tools/website-speed-checker.
# Anonymous category page, 2,000 connections, 120s
wrk -t8 -c2000 -d120s --latency https://example.com/product-category/widgets/
# Product page, ApacheBench for comparison
ab -n 100000 -c 2000 -k https://example.com/product/blue-widget/
# Watch Varnish live during the run
varnishstat -1 -f MAIN.client_req -f MAIN.cache_hit -f MAIN.cache_miss -f MAIN.n_object
# Watch PHP-FPM saturation
ss -s; systemctl status php8.2-fpm | grep -i 'max_children'
Reading the results correctly
Throughput alone misleads. Check three things. First, the Varnish hit ratio: below 85 percent on a catalog site means cookies or query strings are fragmenting the cache. Second, the error rate: any 502 or 504 under load points to PHP-FPM pm.max_children or a backend timeout, not to a caching defect. Third, the latency tail: compare p99, not the mean, since cart traffic drags the tail. Run wrk with --latency and read the 99 percent line. Capture MariaDB's Threads_running during the test; if it stays above CPU core count, Redis is not covering enough of the query load and you should inspect the slow log for uncacheable queries such as ORDER BY RAND().
mysql -e "SHOW GLOBAL STATUS LIKE 'Threads_running';"
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
Why the cart path decides your real capacity
Anonymous Varnish hits make headline numbers look spectacular, but revenue happens on the pass-through routes: add to cart, checkout, payment callbacks. Capacity planning should be based on those routes. In our WooCommerce run, 6 percent of traffic carried a cart cookie yet consumed 71 percent of PHP-FPM time. Raise pm.max_children according to memory, using average worker RSS of 60 to 90 MB, and keep Redis serving sessions and transients so cart operations avoid writing sessions to wp_options in MySQL. Test checkout separately with a scripted wrk Lua flow or k6 so you measure the path that actually limits conversion.
5 Production Catastrophes When Pairing Redis and Varnish on Cloud VPS
Pairing both layers on a small VPS exposes failure modes that single-layer deployments never meet. These five account for most of the incident tickets we have traced. First, the cached cart. A plugin emitted Set-Cookie on a product page, and the VCL stripped it, so one customer's cart fragment was stored and served to the next visitor. Fix by passing on any route that sets session cookies and never unsetting Set-Cookie on responses that contain user data. Second, memory contention. Varnish with 4 GB malloc plus Redis with 2 GB plus MariaDB's 2 GB buffer pool on an 8 GB host triggered the OOM killer, which terminated MariaDB mid-write. Budget total resident memory under 80 percent and use cgroups or systemd MemoryMax to protect the database. Third, the purge storm. A bulk product import fired 14,000 individual PURGE requests, saturating the Varnish management path and stalling PHP while it waited for responses. Batch into a single BAN keyed to a surrogate tag and run the invalidation asynchronously. Fourth, ghost sessions. Storing PHP sessions in Redis with allkeys-lru evicted live sessions under memory pressure, logging users out mid-checkout. Use a separate Redis instance or database for sessions with noeviction, distinct from the cache instance with volatile-lru. Fifth, the stale-forever outage. A long grace combined with a failing health probe let Varnish serve stale pages for 11 hours after a broken deploy, hiding the incident from uptime monitors. Monitor Age headers and alert when the maximum Age exceeds expected TTL plus a margin. Each of these shows why you test with the real configuration and verify headers, not just response codes.
# Pin memory ceilings with systemd drop-ins
systemctl edit mariadb # [Service] MemoryMin=2G
systemctl edit redis-server # [Service] MemoryMax=2500M
systemctl edit varnish # [Service] MemoryMax=4G
# Confirm OOM history
journalctl -k | grep -i 'out of memory'
free -m; cat /proc/$(pidof redis-server)/status | grep VmRSS
# Detect stale-forever: max Age of served pages
curl -sI https://example.com/ | grep -i '^age'
Isolating sessions from the object cache
Run two Redis instances, or at minimum two logical databases with different policies, because the eviction semantics are incompatible. The object cache should evict freely under volatile-lru and tolerate a total flush. The session store must never evict live keys and should use noeviction with a TTL matching session.gc_maxlifetime. Configure PHP with session.save_handler = redis and a save_path pointing at the dedicated socket. Confirm with redis-cli -n 1 dbsize that session keys land where expected, and set an alert on used_memory ratio for the session instance since noeviction fails writes loudly when full.
# php.ini
session.save_handler = redis
session.save_path = "unix:///var/run/redis/redis-sessions.sock?database=1"
session.gc_maxlifetime = 7200
Detecting and preventing cross-user cache leaks
Test for leaks deliberately. Request a product page with a logged-in cookie, then request the same URL without cookies and compare bodies for personal data such as username or cart count. Add a vcl_backend_response rule that refuses to cache any response with Set-Cookie or with a Cache-Control of private, using return (pass) with a hit-for-miss TTL of 120 seconds so the decision is itself cached and does not serialize requests. Grep varnishlog for 'TTL' lines to confirm hit-for-pass creation. Audit third-party plugins after every update, as a new version adding a cookie is the most common trigger for both leaks and sudden hit-ratio collapse.
curl -s -b 'wordpress_logged_in_x=1' https://example.com/shop/ | grep -ci 'my account'
curl -s https://example.com/shop/ | grep -ci 'my account'
varnishlog -g request -q 'RespStatus == 200 and RespHeader:Set-Cookie' -i RespHeader,Hit
Lab Benchmark: 2,000 Concurrent Connections, WooCommerce 8k SKUs on 4 vCPU / 8 GB VPS
| Configuration | Anonymous Req/s | p95 Latency | MySQL Queries/Req | Error Rate |
|---|
| No cache (baseline) | 38 | 41.2 s | 187 | 22.4% |
| Redis object cache (TCP 6379) | 151 | 3.4 s | 31 | 4.1% |
| Redis object cache (Unix socket) | 164 | 2.9 s | 29 | 2.8% |
| Varnish only (cookies stripped, grace 6h) | 21,400 | 14 ms | 0 on hit | 0.0% |
| Varnish + Redis socket + split session store | 21,350 anon / 210 cart | 14 ms anon / 1.9 s cart | 0 anon / 24 cart | 0.0% |
Frequently Asked Questions
Should high-traffic PHP websites use Redis Object Caching or Varnish HTTP Accelerator?
Use both, because they cache different things. Varnish stores complete HTML for anonymous visitors and delivers it in 1 to 5ms without running PHP, which handles most read traffic. Redis caches database results and sessions, protecting MySQL for logged-in users, carts and checkout that Varnish must pass through. If forced to pick one, choose Varnish for mostly anonymous content sites and Redis for application-heavy, personalized stores.
Related Diagnostic Tool: Website Speed & Caching Analyzer