Every time I audit a slow website, caching is near the top of my checklist. It’s one of those unglamorous settings that quietly does a lot of heavy lifting, cutting server load and making repeat visits feel almost instant. In this guide, I’ll explain how Apache caching works and walk through a full setup, from browser caching headers to server-side caching with mod_cache.
What Is Content Caching, and Why Does It Matter?
Caching stores a copy of content so it can be reused for future requests instead of being regenerated or re-fetched from scratch every time. In Apache, there are two main layers of caching to think about:
- Client-side (browser) caching – tells the visitor’s browser to store static assets (images, CSS, JS) locally so it doesn’t need to re-download them on every visit.
- Server-side caching – Apache itself stores generated content (like the output of a dynamic PHP page) in memory or on disk, serving the cached version to subsequent visitors instead of regenerating it each time.
Why it matters:
- Reduced server load – fewer requests hit your application or database layer.
- Faster page loads – cached assets load from local browser storage or Apache’s cache instantly.
- Lower bandwidth usage – repeat visitors don’t re-download unchanged files.
- Better scalability – your server can handle more concurrent visitors with the same hardware.
Prerequisites
- Apache 2.4+ installed with root/sudo access
mod_expiresandmod_headersavailable (both ship with Apache by default)mod_cache,mod_cache_disk, andmod_cache_socachefor server-side caching- Basic understanding of HTTP headers (Cache-Control, Expires, ETag)
Part 1: Browser Caching with mod_expires and mod_headers
Step 1: Enable the Modules
Debian/Ubuntu:
sudo a2enmod expires
sudo a2enmod headers
sudo systemctl restart apache2
CentOS/RHEL:
Ensure these lines exist in httpd.conf:
LoadModule expires_module modules/mod_expires.so
LoadModule headers_module modules/mod_headers.so
Then restart:
sudo systemctl restart httpd
Step 2: Configure Expiration Rules
Add this block to your virtual host file or .htaccess:
ExpiresActive On
# Images
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType image/x-icon "access plus 1 year"
# Video
ExpiresByType video/mp4 "access plus 1 year"
# CSS, JavaScript
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType text/javascript "access plus 1 month"
# Fonts
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType application/vnd.ms-fontobject "access plus 1 year"
# HTML - short cache since content changes often
ExpiresByType text/html "access plus 0 seconds"
# Default
ExpiresDefault "access plus 2 days"
Step 3: Add Cache-Control Headers
Expires headers are somewhat legacy; modern setups pair them with Cache-Control for finer-grained control:
Header set Cache-Control "max-age=31536000, public, immutable"
Header set Cache-Control "max-age=0, no-cache, must-revalidate"
The immutable directive tells modern browsers not to even revalidate the file until it expires — useful for versioned static assets (e.g., style.abc123.css).
Part 2: Server-Side Caching with mod_cache
Server-side caching is especially useful for dynamic content (PHP, proxied backends) that’s expensive to regenerate on every request.
Step 1: Enable Required Modules
sudo a2enmod cache
sudo a2enmod cache_disk
sudo systemctl restart apache2
Step 2: Configure Disk Caching
Add this to your Apache config:
CacheRoot /var/cache/apache2/mod_cache_disk
CacheEnable disk /
CacheDirLevels 2
CacheDirLength 1
CacheMaxFileSize 5000000
CacheMinFileSize 1
CacheIgnoreHeaders Set-Cookie
CacheDefaultExpire 3600
CacheMaxExpire 86400
CacheLastModifiedFactor 0.5
Create the cache directory and set proper permissions:
sudo mkdir -p /var/cache/apache2/mod_cache_disk
sudo chown -R www-data:www-data /var/cache/apache2/mod_cache_disk
On CentOS/RHEL, the Apache user is typically apache instead of www-data.
Step 3: Restart and Test
sudo apachectl configtest
sudo systemctl restart apache2
Verify caching is active by checking response headers:
curl -I https://yourdomain.com
Look for an X-Cache or Age header, depending on your configuration, indicating whether the response came from cache.
Using mod_cache_socache for Shared Memory Caching
For high-traffic sites, an in-memory cache is faster than disk. mod_cache_socache works with a shared object cache provider like mod_socache_shmcb:
sudo a2enmod cache_socache
sudo a2enmod socache_shmcb
CacheSocache shmcb
CacheSocacheMaxSize 102400
CacheEnable socache /
Memory caches are faster but limited by available RAM and cleared on restart, so they’re best combined with disk caching as a fallback.
Real-World Use Cases
- WordPress and CMS-driven sites – caching rendered HTML pages avoids expensive PHP/database calls on every request.
- Reverse proxy setups – Apache configured as a reverse proxy in front of application servers (Node.js, Python) benefits enormously from caching proxied responses.
- Static asset-heavy sites – portfolios, marketing sites, and documentation sites with lots of images and fonts see immediate load-time improvements.
- API gateways – caching read-heavy, infrequently changing API responses reduces backend load significantly.
Common Mistakes to Avoid
- Caching sensitive or personalized content – Never cache pages containing user-specific data (account pages, checkout) without proper
VaryandCache-Control: privateheaders. - Setting cache lifetimes too long on HTML – HTML often changes; caching it for a year like static assets leads to stale content.
- Forgetting cache busting for updated assets – Use versioned filenames (e.g.,
app.v2.js) or query strings so browsers fetch new versions after deployment. - Not excluding cookies from cached responses – Failing to set
CacheIgnoreHeaders Set-Cookiecan cause one user’s session cookie to be served to another. - Ignoring cache invalidation – Have a plan (cache-clear command, TTL, or deployment hook) for purging stale cached content.
Security Best Practices
- Never cache authenticated or personalized pages without explicit
privatecache directives. - Set
CacheIgnoreHeaders Set-Cookieto avoid leaking session data between users. - Restrict write access to the cache directory to the Apache user only.
- If using a CDN alongside Apache caching, ensure cache purge mechanisms are coordinated to avoid serving outdated or sensitive content.
Performance Optimization Tips
- Combine caching with compression (see our companion Apache compression guide) — compress once, cache the compressed output.
- Use long
max-agevalues with immutable, versioned filenames for static assets. - Monitor cache hit ratios; a low hit ratio may mean your cache rules or TTLs need adjustment.
- Consider a dedicated reverse proxy cache (like Varnish) in front of Apache for very high-traffic sites, using Apache’s caching as a secondary layer.
Troubleshooting
Cache not being used:
- Confirm
mod_cacheandmod_cache_diskare enabled withapache2ctl -M. - Check that
CacheEnablematches the correct URL path. - Ensure the cache directory has correct write permissions.
Stale content being served:
- Lower
CacheMaxExpireor manually clear the cache directory:sudo rm -rf /var/cache/apache2/mod_cache_disk/*. - Verify your application isn’t sending conflicting cache headers that override Apache’s rules.
Disk filling up:
- Set
CacheMaxFileSizeto a reasonable limit and monitor disk usage; old cache entries are cleaned up automatically but aCacheQuickHandlermisconfiguration can sometimes bypass expiry logic.
Frequently Asked Questions
What’s the difference between Expires and Cache-Control? Expires sets an absolute date/time; Cache-Control uses relative directives like max-age and offers more granular control (public/private, no-cache, immutable). Modern setups favor Cache-Control, with Expires as a fallback for older clients.
Should I cache HTML pages? Static HTML can be cached briefly; dynamically generated HTML (like a CMS homepage) can be cached server-side with mod_cache but usually with a short TTL to balance freshness and performance.
Does caching work with HTTPS? Yes, caching operates independently of TLS termination and works the same way over HTTPS.
How do I clear the Apache cache? For disk caching, remove the contents of the CacheRoot directory. For browser caching, that’s controlled by the client, though cache-busting filenames force a refresh.
Summary and Key Takeaways
- Apache caching operates on two levels: browser caching (mod_expires, mod_headers) and server-side caching (mod_cache).
- Set long cache lifetimes for static, versioned assets and short or no caching for dynamic HTML.
- Use Cache-Control alongside Expires for the best browser compatibility.
- Server-side caching with mod_cache_disk or mod_cache_socache reduces load on backend applications significantly.
- Always exclude cookies and personalized content from cached responses.
- Monitor cache hit rates and have a clear invalidation strategy for deployments.