Website Caching Explained: From Browser Cache to Redis
Caching is the single biggest performance win for most websites. A properly cached WordPress site serves pages in 30ms instead of 800ms. This guide covers every caching layer, what it does, how to configure it, and when to use it.

Web hosting enthusiast who tests providers and breaks down features, pricing, and real world speed

Impact at a Glance
| Caching Layer | Speed Gain | Difficulty | Cost |
|---|---|---|---|
| Browser Cache | 50–80% fewer requests | Easy | Free |
| CDN Cache | 30–70% faster globally | Easy | Free: $20/mo |
| Full-Page Cache | 10–20x faster TTFB | Easy | Free |
| OPcache | 2–3x faster PHP | Easy | Free |
| Redis Object Cache | 30–50% faster uncached | Medium | $0–10/mo |
| DB Query Cache | 10–20% faster queries | Advanced | Free |
What Is Caching?
Caching stores a copy of data in a faster-to-access location so future requests can be served without repeating expensive operations. Instead of executing PHP code, querying a database, and assembling HTML on every page view, a cache serves a pre-built result in milliseconds.
Without caching, every visitor triggers the full processing pipeline: DNS lookup → TCP connection → PHP execution → database queries → HTML generation → response. With caching, most of this work is skipped entirely.
| Metric | No Caching | Full Caching Stack |
|---|---|---|
| TTFB | 300–2,000 ms | 20–50 ms |
| Full Page Load | 2–8 seconds | 0.5–1.5 seconds |
| Database Queries/Page | 30–200 | 0 (served from cache) |
| PHP Execution Time | 200–800 ms | 0 ms (bypassed) |
| Server CPU Per Request | High | Minimal |
| Max Concurrent Users (2GB VPS) | 50–100 | 500–2,000 |
| Monthly Bandwidth | 100% | 30–50% (CDN offloads) |
The 6 Caching Layers
A complete caching strategy uses multiple layers, each targeting a different part of the request lifecycle. Here's the order a request passes through, from closest to the user to closest to the database.
Browser Cache
Stores static files (CSS, JS, images) on the user's device. Eliminates network requests for returning visitors.
CDN Cache
Caches content on edge servers worldwide. Reduces latency by serving from the nearest location. Handles 60–90% of static requests.
Full-Page Cache
Stores complete HTML pages on the server. Bypasses PHP and database entirely. The single biggest speed win.
OPcache
Caches compiled PHP bytecode in memory. Eliminates re-parsing PHP files on every request. Built into PHP.
Object Cache (Redis)
Stores database query results and computed objects in RAM. Reduces database load by 50–90% for dynamic pages.
Database Query Cache
MySQL/MariaDB caches identical query results. Lowest-level cache, often superseded by Redis.
Browser Cache
Browser caching tells the visitor's browser to store static files locally. On subsequent visits, the browser loads CSS, JavaScript, images, and fonts from disk instead of downloading them again. This eliminates 50–80% of network requests for returning visitors.
Nginx, recommended cache headers
# /etc/nginx/conf.d/cache-headers.conf
# Static assets, cache for 1 year (they use hashed filenames)
location ~* \.(css|js|woff2|woff|ttf|eot)$ {
expires 365d;
add_header Cache-Control "public, immutable";
add_header Vary "Accept-Encoding";
}
# Images, cache for 6 months
location ~* \.(jpg|jpeg|png|gif|webp|avif|svg|ico)$ {
expires 180d;
add_header Cache-Control "public";
}
# HTML, short cache with revalidation
location ~* \.html$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
# API responses, no cache
location /api/ {
add_header Cache-Control "no-store, no-cache, must-revalidate";
}Apache, .htaccess cache rules
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/webp "access plus 6 months"
ExpiresByType image/jpeg "access plus 6 months"
ExpiresByType image/png "access plus 6 months"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType text/html "access plus 1 hour"
</IfModule>
<IfModule mod_headers.c>
# Immutable for hashed assets
<FilesMatch "\.(css|js|woff2)$">
Header set Cache-Control "public, immutable, max-age=31536000"
</FilesMatch>
</IfModule>| Header | Purpose | Example |
|---|---|---|
| Cache-Control | Primary directive for caching behavior | public, max-age=31536000, immutable |
| Expires | Legacy fallback (HTTP/1.0) | Thu, 01 Jan 2027 00:00:00 GMT |
| ETag | Fingerprint for conditional requests | "5d8c72a5edda8" |
| Last-Modified | Timestamp for conditional requests | Mon, 15 Jul 2026 12:00:00 GMT |
| Vary | Cache key modifiers | Accept-Encoding |
CDN Cache
A Content Delivery Network caches your content on edge servers in 100+ locations worldwide. Instead of every visitor hitting your origin server, 60–90% of requests are served from the nearest edge node, reducing latency from 200ms to 20ms for distant visitors.
| CDN Provider | Edge Locations | Free Tier | Full-Page Cache | Best For |
|---|---|---|---|---|
| Cloudflare | 310+ | Yes (generous) | Yes (Page Rules) | Most websites |
| Bunny CDN | 116+ | 14-day trial | Yes (Perma-Cache) | Budget-conscious |
| AWS CloudFront | 450+ | 1TB/mo free (12 mo) | Yes (behaviors) | AWS-hosted apps |
| Fastly | 90+ | No | Yes (VCL) | Enterprise / real-time |
| KeyCDN | 60+ | No | Limited | Simple static sites |
Pro tip: Cloudflare's free tier with APO (Automatic Platform Optimization, $5/mo) caches entire WordPress pages on the edge, turning every page load into a CDN cache hit. It's the single easiest performance upgrade for WordPress sites.
Full-Page Cache
Full-page caching stores the complete HTML output of a page so subsequent requests skip PHP execution and database queries entirely. This is the most impactful single optimization for dynamic sites, reducing TTFB from 300–800ms to 20–50ms.
Nginx FastCGI Cache, server-level page caching
# In nginx.conf (http block)
fastcgi_cache_path /var/cache/nginx levels=1:2
keys_zone=WORDPRESS:100m inactive=60m max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
# In your server block
set $skip_cache 0;
# Don't cache POST requests
if ($request_method = POST) { set $skip_cache 1; }
# Don't cache URLs with query strings
if ($query_string != "") { set $skip_cache 1; }
# Don't cache logged-in users
if ($http_cookie ~* "wordpress_logged_in") { set $skip_cache 1; }
# Don't cache WooCommerce pages
if ($request_uri ~* "/cart|/checkout|/my-account") { set $skip_cache 1; }
location ~ \.php$ {
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 60m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-FastCGI-Cache $upstream_cache_status;
# ... standard PHP-FPM config
}| Method | Speed | Complexity | Best For |
|---|---|---|---|
| Nginx FastCGI Cache | Fastest (10–20ms TTFB) | Medium (server config) | VPS / dedicated servers |
| LiteSpeed Cache (LSAPI) | Very fast (15–25ms TTFB) | Easy (plugin) | LiteSpeed servers |
| Varnish Reverse Proxy | Very fast (10–20ms TTFB) | High (VCL config) | High-traffic sites |
| WP Super Cache (plugin) | Fast (30–50ms TTFB) | Very easy | Shared hosting / beginners |
| W3 Total Cache (plugin) | Fast (30–50ms TTFB) | Medium (many options) | Advanced WordPress users |
| WP Rocket (plugin) | Fast (25–40ms TTFB) | Very easy | WordPress (paid, $59/yr) |
Opcode Cache (OPcache)
Every time PHP processes a file, it parses the source code into bytecode, then executes it. OPcache stores the compiled bytecode in shared memory, eliminating the parsing step on every request. This gives a 2–3x PHP execution speed improvement with zero code changes.
Recommended OPcache settings (php.ini)
; Enable OPcache opcache.enable=1 opcache.enable_cli=0 ; Memory, 256MB handles most WordPress sites + plugins opcache.memory_consumption=256 ; Max cached files (WordPress + plugins typically need 10,000+) opcache.max_accelerated_files=20000 ; How often to check for file changes (seconds) ; Production: 60+ (files rarely change) ; Development: 0 (check every request) opcache.revalidate_freq=60 ; Don't waste memory on comments (unless using annotations) opcache.save_comments=1 ; Huge pages (if supported by your OS) opcache.huge_code_pages=1 ; Preloading (PHP 8.0+), loads framework files at startup ; opcache.preload=/var/www/html/preload.php ; opcache.preload_user=www-data
Check OPcache status
# Check if OPcache is enabled php -i | grep opcache.enable # View detailed stats (create a PHP file) <?php $status = opcache_get_status(); echo "Memory used: " . round($status['memory_usage']['used_memory'] / 1024 / 1024, 2) . " MB\n"; echo "Hit rate: " . round($status['opcache_statistics']['opcache_hit_rate'], 2) . "%\n"; echo "Cached files: " . $status['opcache_statistics']['num_cached_scripts'] . "\n";
Object Cache: Redis vs Memcached
Object caching stores the results of expensive database queries and computed data in RAM. Instead of re-running the same SQL queries on every page load, the application checks Redis/Memcached first. This reduces database load by 50–90% and speeds up uncached page generation by 30–50%.
| Feature | Redis | Memcached |
|---|---|---|
| Data Types | Strings, lists, sets, hashes, sorted sets | Strings only |
| Persistence | Yes (RDB + AOF) | No (RAM only) |
| Max Value Size | 512 MB | 1 MB |
| Replication | Built-in (master-replica) | No |
| Pub/Sub | Yes | No |
| Lua Scripting | Yes | No |
| Memory Efficiency | Slightly higher overhead | Lower per-key overhead |
| Multi-threading | Single-threaded (io-threads in 6.0+) | Multi-threaded |
| Best For | WordPress, Laravel, Django | Simple key-value caching |
| WordPress Plugin | Redis Object Cache (free) | Memcached plugin |
Install Redis and connect to WordPress
# Install Redis
sudo apt install redis-server -y
# Secure Redis, bind to localhost only
sudo nano /etc/redis/redis.conf
# Set: bind 127.0.0.1 ::1
# Set: maxmemory 128mb
# Set: maxmemory-policy allkeys-lru
# Start and enable
sudo systemctl enable redis-server
sudo systemctl start redis-server
# Test connection
redis-cli ping # Should return PONG
# Add to wp-config.php (before "That's all, stop editing!")
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);
# Then install "Redis Object Cache" plugin and click "Enable Object Cache"Database Query Cache
MySQL's built-in query cache stored identical query results and returned them without re-executing. However, MySQL 8.0 removed the query cache because it caused contention under high concurrency. MariaDB still supports it.
Modern Alternatives to MySQL Query Cache
- Redis object cache: caches query results at the application layer with fine-grained TTL control
- ProxySQL: query caching reverse proxy with regex-based rules and connection pooling
- InnoDB buffer pool: increase size to cache hot data pages in memory (set to 70–80% of available RAM)
- MariaDB query cache: still available on MariaDB 10.x with improved invalidation
Tune InnoDB buffer pool (MySQL/MariaDB)
# Check current buffer pool size mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';" # Set to 70-80% of available RAM (for dedicated DB server) # In /etc/mysql/mysql.conf.d/mysqld.cnf: innodb_buffer_pool_size = 1G # For 2GB RAM server innodb_buffer_pool_instances = 1 # 1 per GB (up to 8) innodb_log_file_size = 256M # Check buffer pool hit ratio (should be >99%) mysql -e "SHOW STATUS LIKE 'Innodb_buffer_pool_read%';" # Hit ratio = 1 - (reads / read_requests) * 100
Cache Invalidation Strategies
"There are only two hard things in Computer Science: cache invalidation and naming things.": Phil Karlton. Serving stale content is worse than serving slow content. Here are the proven strategies:
| Strategy | How It Works | Best For |
|---|---|---|
| Time-Based (TTL) | Cache expires after a set duration (e.g., 60 minutes) | Blog posts, static pages |
| Event-Based Purge | Cache cleared when content is updated (post save, product update) | CMS content, eCommerce |
| Cache Tags / Groups | Tag cached items by category, purge all items with a specific tag | Complex applications |
| Stale-While-Revalidate | Serve stale cache while fetching fresh content in background | High-traffic APIs |
| Cache Busting (versioned URLs) | Append hash to filename (style.abc123.css), new file = new cache | CSS, JS, images |
| Surrogate Keys (CDN) | Tag CDN cache entries, purge by key via API | Fastly, Cloudflare Enterprise |
WordPress Caching: Complete Setup
WordPress benefits enormously from caching because it runs PHP and queries MySQL on every page load by default. Here's the recommended caching stack in priority order:
| Priority | Layer | Recommended Tool | Impact |
|---|---|---|---|
| 1 | Page Cache | WP Rocket ($59/yr) or LiteSpeed Cache (free) | 10–20x TTFB reduction |
| 2 | CDN | Cloudflare (free) + APO ($5/mo) | 30–70% faster globally |
| 3 | OPcache | Built-in (verify enabled) | 2–3x PHP execution |
| 4 | Object Cache | Redis + Redis Object Cache plugin (free) | 30–50% fewer DB queries |
| 5 | Image Optimization | ShortPixel or Imagify | 40–70% smaller images |
| 6 | Browser Caching | Set via .htaccess or hosting panel | 50–80% fewer requests |
Don't stack multiple page caching plugins. Using WP Super Cache + W3 Total Cache + WP Rocket together causes conflicts, double-caching, and broken pages. Pick one page cache plugin and stick with it.
Hosting Provider Caching Support
Many managed hosts include built-in caching, saving you from configuring it yourself. Here's what each provider offers out of the box.
| Provider | Page Cache | CDN | Redis | OPcache |
|---|---|---|---|---|
| Kinsta | Nginx FastCGI | Cloudflare Enterprise | Yes (all plans) | Yes |
| Cloudways | Varnish + Breeze | Cloudflare (add-on) | Yes (1-click) | Yes |
| SiteGround | SuperCacher (Nginx) | Cloudflare (free) | Yes (GoGeek+) | Yes |
| WP Engine | EverCache (proprietary) | Global CDN included | Yes (custom) | Yes |
| Hostinger | LiteSpeed Cache | Cloudflare (built-in) | No (shared) | Yes |
| A2 Hosting | LiteSpeed / Turbo | Cloudflare (free) | Yes (Turbo plans) | Yes |
| DigitalOcean (DIY) | Manual setup | Manual setup | Manual setup | Manual |
| Bluehost | Plugin-based | Cloudflare (built-in) | No | Yes |
Common Caching Mistakes
❌ Caching logged-in user pages
✅ Always exclude logged-in users from page cache. Cached user-specific content (cart, dashboard) can leak to other visitors, a serious privacy issue.
❌ Stacking multiple page cache plugins
✅ Use only ONE page caching solution. Multiple plugins create conflicting cache files, doubled memory usage, and broken invalidation.
❌ Setting OPcache revalidation to 0 in production
✅ In production, set opcache.revalidate_freq=60 or higher. Checking for file changes on every request defeats the purpose.
❌ Not purging cache after updates
✅ Configure automatic cache purging on post save, theme update, and plugin update. Stale cache = stale content = confused visitors.
❌ Caching 404 pages and error responses
✅ Only cache 200 OK responses. Caching 404s can prevent new content from appearing after publishing.
❌ Using Redis without maxmemory or eviction policy
✅ Always set maxmemory (128MB minimum) and maxmemory-policy allkeys-lru. Without limits, Redis can consume all RAM and crash your server.
❌ Forgetting to warm the cache after purging
✅ After a full cache purge, the first visitor to each page gets a slow uncached response. Use a cache warming script or crawler (e.g., WP Rocket's preload) to rebuild cache proactively.
Caching Implementation Checklist
| Action | Priority | Expected Gain |
|---|---|---|
| Enable full-page caching | Critical | 10–20x faster TTFB |
| Enable OPcache with 256MB memory | Critical | 2–3x faster PHP |
| Set browser cache headers for static assets | Critical | 50–80% fewer requests |
| Add Cloudflare CDN (free tier) | High | 30–70% faster globally |
| Install Redis object cache | High | 30–50% fewer DB queries |
| Configure cache invalidation rules | High | Prevents stale content |
| Exclude dynamic pages from cache | High | Prevents data leaks |
| Tune InnoDB buffer pool (70–80% RAM) | High | Fewer disk reads |
| Enable cache preloading / warming | Medium | No cold-cache penalty |
| Set up Gzip/Brotli compression | Medium | 60–80% smaller transfers |
| Monitor cache hit rates | Medium | Verify caching works |
| Test site as logged-in vs logged-out user | Medium | Catch caching bugs |
| Review cache settings after plugin updates | Low | Prevent conflicts |
Frequently Asked Questions
What is the most important type of caching for website speed?
What's the difference between Redis and Memcached?
Does caching affect SEO?
How do I know if my caching is working?
Should I cache dynamic content like shopping carts?
Can too much caching cause problems?
What's the difference between browser cache and CDN cache?
How much RAM does Redis need for WordPress?
Need Hosting With Built-In Caching?
Skip the manual setup. Many managed hosts include page caching, CDN, Redis, and OPcache out of the box. Find the fastest option for your site.
Compare Fast Hosting