Help us fix this page
If you found a broken link, missing page, or incorrect redirect, please let us know. Your report helps us improve the website for everyone.

Speed and efficiency are critical factors in web development that directly influence user experience and business success. Users expect websites to load within milliseconds, and delays cause frustration, higher bounce rates, and lost revenue. Caching remains one of the most effective techniques for achieving faster load times and improving server performance.
This guide covers the fundamentals of caching, explores modern caching strategies for 2026, and provides practical implementation guidance for Apache and Nginx. The content reflects current best practices, including HTTP/3 adoption, microcaching, and emerging edge computing patterns.
stale-while-revalidate is a critical directive for modern caching, enabling stale content to be served while the cache is refreshed in the background. This eliminates origin fetches on cache expiration without serving stale content beyond an acceptable window.Caching stores copies of files or data in a temporary storage location (a cache). When a request for data is made, the cache is checked first. If the data is found (a cache hit), it is served directly from the cache. If not found (a cache miss), the data is fetched from the original source, stored in the cache for future use, and then delivered.
Modern web architectures typically employ multiple layers of caching:
| Cache Type | Location | Purpose |
|---|---|---|
| Browser cache | User’s device | Stores resources (HTML, CSS, JavaScript, images) for faster repeat visits |
| CDN cache | Edge locations | Distributes cached content geographically to reduce latency |
| Web server cache | Server side | Reduces regeneration of resources on each request |
| Application cache | Application layer | Caches data retrieved from databases or APIs |
| Database cache | Database layer | Stores frequently accessed queries (Redis, Memcached) |
| Memory cache | RAM | Provides extremely fast access times |
The protocol stack underneath web delivery has shifted meaningfully. HTTP/3 and QUIC now account for over 40% of all CDN-served traffic. QUIC’s zero‑RTT resumption and multiplexed streams change the math on when edge caching helps and when it adds a hop. Edge compute runtimes (V8 isolates, Wasm workers) have matured to the point where meaningful logic can run at the edge without cold‑start penalties exceeding 5ms. The boundary between “static” and “dynamic” content continues to shift toward the edge.
HTTP caching is one of the most fundamental web caching mechanisms. It allows browsers and intermediate caches to store resources based on instructions from the server via HTTP headers.
The Cache-Control header is the most important caching header. It effectively “switches on” caching in the browser. Without this header set, no other caching headers will yield results.
Key directives:
| Directive | Purpose | Example |
|---|---|---|
| max-age | Maximum time (seconds) a resource is fresh | max-age=3600 |
| s-maxage | Cache duration for CDN/shared caches only | s-maxage=86400 |
| stale-while-revalidate | Serve stale content while refreshing in background | stale-while-revalidate=86400 |
| no-store | Prevent any caching (sensitive data) | no-store |
| public | Cacheable by any cache (CDN, proxy, browser) | public |
| private | Cacheable only by the end‑user’s browser | private |
| immutable | Resource will never change (hashed assets) | immutable |
According to a 2026 Vercel guide, the right Cache-Control value depends on what you are caching and how fresh it needs to be:
| Content Type | Recommended Header | Use Case |
|---|---|---|
| Server‑rendered, same for all visitors | max-age=0, s-maxage=86400 | Pages where every visitor sees the same content. A safe starting point. |
| Semi‑static (product pages, blogs) | max-age=120, s-maxage=86400 | Content that tolerates short staleness. A 60‑120s browser TTL reduces edge requests for return visitors. |
| Personalised or per‑user | private, max-age=0 | Responses that vary by cookie, session, or auth. private prevents CDN caching. |
| Immutable static assets (hashed JS, CSS) | max-age=31536000, immutable | Content‑hashed assets that never change. |
The Expires header provides an absolute date and time after which the resource is stale. When both Expires and max-age are set, max-age takes precedence.
These headers enable conditional requests-the browser can ask the server if the cached resource is still valid:
If-None-Match header.If-Modified-Since header.If the resource hasn’t changed, the server responds with 304 Not Modified, saving bandwidth.
The Vary header prevents cache fragmentation issues. A single misconfigured Vary: Accept-Encoding, Cookie, User-Agent header can fragment a cache into thousands of useless variants. Use Vary only when necessary and keep the list of headers minimal.
A multi‑tier caching strategy pairs a CDN with a dedicated mid‑tier cache, delivering higher and more durable origin offload, more consistent performance, and better economics-especially for applications with long‑tail or dynamic content.
Typical tiers:
Microcaching (1–10 seconds) is effective for dynamic pages that receive heavy bursts of traffic but do not change constantly. A 1‑10 second cache TTL can significantly reduce origin load while maintaining near‑real‑time content freshness.
When to use microcaching:
The stale-while-revalidate directive allows stale content to be served while the cache is refreshed in the background. This eliminates origin fetches on cache expiration without serving stale content beyond an acceptable window.
Example:
Cache-Control: max-age=60, stale-while-revalidate=86400This caches the resource for 60 seconds, then serves stale content for up to 24 hours while revalidating in the background.
HTTP/3 and QUIC now account for over 40% of all CDN‑served traffic. Modern caching strategies must account for QUIC’s zero‑RTT resumption and multiplexed streams, which change the performance characteristics of edge caching.
Implications for caching:
Apache supports caching via the mod_cache, mod_cache_disk, and mod_expires modules.
On Debian/Ubuntu systems:
sudo a2enmod cache
sudo a2enmod cache_disk
sudo a2enmod expires
sudo a2enmod headers
sudo a2enmod deflate
sudo systemctl restart apache2On CentOS/RHEL systems, load modules in /etc/httpd/conf/httpd.conf or /etc/httpd/conf.modules.d/*.conf:
apache
<IfModule mod_cache.c>
CacheQuickHandler off
CacheLock on
CacheLockPath /tmp/mod_cache-lock
CacheLockMaxAge 5
CacheIgnoreHeaders Set-Cookie
</IfModule>
<IfModule mod_cache_disk.c>
CacheRoot /var/cache/apache2/mod_cache_disk
CacheEnable disk /
CacheDirLevels 2
CacheDirLength 1
CacheDefaultExpire 3600
CacheMaxExpire 86400
CacheIgnoreHeaders Set-Cookie
</IfModule>Key configuration notes for 2026:
mod_cache is not a “one‑click” caching solution-it must be paired with a storage backend (like mod_cache_disk) and strict response header rulesmod_cache alone does not cache dynamic responses; it must be paired with mod_proxy to create a “proxy + cache” chainpublic with max-age in Cache-Control, and avoid Set-Cookie or Vary: * headers<IfModule mod_expires.c>
ExpiresActive On
# Default expiry
ExpiresDefault "access plus 1 day"
# Static assets (long cache)
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType text/css "access plus 30 days"
ExpiresByType application/javascript "access plus 30 days"
ExpiresByType image/ico "access plus 1 month"
ExpiresByType image/x-icon "access plus 1 month"
# HTML (short cache)
ExpiresByType text/html "access plus 1 hour"
</IfModule><IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css \
application/javascript application/json \
image/svg+xml application/xml
</IfModule>sudo apachectl -t
sudo systemctl reload apache2Nginx provides robust caching capabilities through its proxy_cache module.
In the http block of your Nginx configuration:
http {
proxy_cache_path /data/nginx/cache
keys_zone=mycache:10m
max_size=10g
inactive=60m
use_temp_path=off;
# Cache loader settings (avoids startup slowdown)
proxy_cache_path /data/nginx/cache
keys_zone=mycache:10m
loader_threshold=300
loader_files=200;
}Key notes:
keys_zone stores cache metadata (keys and object headers) at roughly 8KB per objectmax_size parameter limits total cached response dataloader_threshold and loader_files to avoid startup slowdownserver {
proxy_cache mycache;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 302 60m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
proxy_cache_background_update on;
proxy_cache_lock on;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}For browser caching, add headers in the location block:
location ~* \.(jpg|jpeg|png|gif|ico|css|js|webp)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location ~* \.html$ {
expires 1h;
add_header Cache-Control "public, max-age=3600";
}For dynamic pages with high traffic bursts:
location /dynamic/ {
proxy_cache mycache;
proxy_cache_valid 200 5s; # Microcaching: 5 seconds
proxy_cache_use_stale updating;
proxy_cache_background_update on;
proxy_pass http://backend;
}Brotli provides better compression ratios than Gzip where supported:
brotli on;
brotli_types text/html text/plain text/css text/xml
application/javascript application/json
image/svg+xml application/xml;
brotli_comp_level 6;
brotli_static on;For modern browsers supporting HTTP/3:
listen 443 quic reuseport;
listen 443 ssl http2;
add_header Alt-Svc 'h3=":443"; ma=86400';| Benefit | Impact |
|---|---|
| Improved performance | Cached content eliminates network latency entirely |
| Reduced server load | Fewer requests reach the origin server |
| Lower bandwidth costs | CDN caching reduces data transmission |
| Better user experience | Faster load times increase retention and conversion |
| Increased reliability | Caching helps prevent server overload during traffic spikes |
| Problem | Impact |
|---|---|
| Slower page load times | Every request must be processed by the origin |
| Increased server load | Higher CPU, memory, and disk usage |
| Higher infrastructure costs | More resources needed to handle load |
| Poor user experience | Increased bounce rates, lost conversions |
| Potential downtime | Server crashes under heavy load |
| Mistake | Impact | Fix |
|---|---|---|
Setting max-age too low on immutable assets | Unnecessary origin fetches | Set max-age=31536000 for content‑hashed assets |
Failing to configure stale-while-revalidate | Cache misses on expiration | Add a stale-while-revalidate window |
Ignoring Vary header explosions | Cache fragmentation | Minimise Vary headers |
Not using private for personalised responses | CDN caches user‑specific data | Use private, max-age=0 |
| Caching HTML pages for long periods | Users see stale content | Keep HTML cache short (1 hour or less) |
| Skipping Brotli compression | Larger file transfers | Enable Brotli where supported |
stale-while-revalidate is a critical directive for modern caching, eliminating origin fetches on cache expiration.max-age=31536000, immutable.mod_cache requires mod_proxy for dynamic content caching-standalone mod_cache_disk alone is insufficient for API or dynamic responses.keys_zone stores cache metadata at roughly 8KB per object-plan shared memory size accordingly.Caching remains one of the highest‑leverage performance optimisations available. A correctly cached response eliminates network latency entirely, reduces server load, and improves perceived performance by orders of magnitude. In 2026, the caching landscape has evolved significantly with HTTP/3 and QUIC adoption, edge compute maturation, and the emergence of multi‑tier caching strategies.
Modern caching requires a layered approach: browser caching with appropriate Cache-Control headers, CDN caching with s-maxage and stale-while-revalidate, application‑level caching with Redis or Memcached, and server‑side caching with Apache mod_cache or Nginx proxy_cache. Each layer serves a specific purpose, and the right combination depends on your content type, traffic patterns, and performance requirements.
The cost of getting caching wrong is significant: unnecessary origin load, higher infrastructure costs, poor user experience, and potential downtime. Conversely, getting caching right delivers measurable performance improvements-sub‑20ms TTFB for static assets and LCP under 1.2 seconds for global users.
Need help implementing caching for your website? Playful Sparkle has been engineering digital products since 2004, offering Web Development, Performance Optimisation, and Infrastructure services. Our team can help you design and implement a multi‑tier caching strategy that delivers measurable performance improvements. Contact us to discuss how we can help you optimise your website’s performance.