How to Set Up Apache for Content Caching

How to set up Apache for content caching

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:

  1. 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.
  2. 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_expires and mod_headers available (both ship with Apache by default)
  • mod_cache, mod_cache_disk, and mod_cache_socache for 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

  1. Caching sensitive or personalized content – Never cache pages containing user-specific data (account pages, checkout) without proper Vary and Cache-Control: private headers.
  2. Setting cache lifetimes too long on HTML – HTML often changes; caching it for a year like static assets leads to stale content.
  3. Forgetting cache busting for updated assets – Use versioned filenames (e.g., app.v2.js) or query strings so browsers fetch new versions after deployment.
  4. Not excluding cookies from cached responses – Failing to set CacheIgnoreHeaders Set-Cookie can cause one user’s session cookie to be served to another.
  5. 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 private cache directives.
  • Set CacheIgnoreHeaders Set-Cookie to 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-age values 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_cache and mod_cache_disk are enabled with apache2ctl -M.
  • Check that CacheEnable matches the correct URL path.
  • Ensure the cache directory has correct write permissions.

Stale content being served:

  • Lower CacheMaxExpire or 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 CacheMaxFileSize to a reasonable limit and monitor disk usage; old cache entries are cleaned up automatically but a CacheQuickHandler misconfiguration 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.

References

Total
3
Shares

Leave a Reply

Previous Post
How to enable server-side scripting in Apache

How to Enable Server-Side Scripting in Apache

Next Post
How to configure Apache for content compression

How to Configure Apache for Content Compression

Related Posts