Setting Up Multi-Level Caching (Browser → CDN → Varnish → Redis → DB)
Multi-level caching reduces response time and database load through sequential storage layers. Each level serves requests without passing them to the next — the earlier the cache hit, the faster the response.
Levels Diagram
Browser Cache (0ms)
↓ miss
CDN (5-30ms, edge nodes)
↓ miss
Varnish (1-5ms, application layer)
↓ miss
Redis (1-5ms, application cache)
↓ miss
Database (5-100ms)
Level 1: Browser Cache
# nginx: headers for browser cache
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML pages — don't cache in browser, but allow CDN
location ~* \.html$ {
add_header Cache-Control "public, max-age=0, s-maxage=300";
add_header Vary "Accept-Encoding, Accept-Language";
}
# API responses
location /api/ {
add_header Cache-Control "private, no-store";
}
immutable — browser doesn't make conditional requests for files with version hashes.
Level 2: CDN (Cloudflare / CloudFront)
# cloudflare page rules
- pattern: "*.company.com/assets/*"
settings:
cache_level: cache_everything
edge_cache_ttl: 2592000 # 30 days
browser_cache_ttl: 31536000 # 1 year
- pattern: "*.company.com/*.html"
settings:
cache_level: cache_everything
edge_cache_ttl: 300
browser_cache_ttl: 0
CloudFront invalidation on deploy:
aws cloudfront create-invalidation \
--distribution-id $DISTRIBUTION_ID \
--paths "/assets/*" "/*.html" "/sitemap.xml"
Level 3: Varnish
# /etc/varnish/default.vcl
vcl 4.1;
backend default {
.host = "app-server";
.port = "8080";
.probe = {
.url = "/health";
.timeout = 1s;
.interval = 5s;
.window = 5;
.threshold = 3;
}
}
sub vcl_recv {
# Don't cache authorized requests
if (req.http.Authorization || req.http.Cookie ~ "session") {
return (pass);
}
# Don't cache POST/PUT/DELETE
if (req.method != "GET" && req.method != "HEAD") {
return (pass);
}
# Remove unnecessary cookies for cached pages
unset req.http.Cookie;
return (hash);
}
sub vcl_backend_response {
# Cache successful responses
if (beresp.status == 200 || beresp.status == 301) {
if (beresp.http.Content-Type ~ "text/html") {
set beresp.ttl = 5m;
} else if (beresp.http.Content-Type ~ "application/json") {
set beresp.ttl = 1m;
}
unset beresp.http.Set-Cookie;
}
# Grace period — return stale cache if backend unavailable
set beresp.grace = 1h;
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
set resp.http.X-Cache-Hits = obj.hits;
}
Invalidation via PURGE:
curl -X PURGE -H "Host: company.com" http://varnish:6081/page/about
Level 4: Redis
from redis import Redis
import json, hashlib
redis = Redis(host='redis', decode_responses=True)
class MultiLevelCache:
def get(self, key: str, db_fn, ttl: int = 300):
# L1: Redis
cached = redis.get(f"cache:{key}")
if cached:
return json.loads(cached), 'redis'
# L2: Database
data = db_fn()
if data is not None:
redis.setex(f"cache:{key}", ttl, json.dumps(data))
return data, 'db'
def invalidate(self, key: str):
redis.delete(f"cache:{key}")
# Invalidate Varnish
self._purge_varnish(key)
def _purge_varnish(self, path: str):
import requests
try:
requests.request('PURGE', f"http://varnish:6081{path}",
headers={'Host': 'company.com'}, timeout=2)
except:
pass
Coordination of Invalidation Between Levels
When data changes, invalidate all levels:
def on_product_updated(product_id):
# Redis
redis.delete(f"product:{product_id}")
redis.delete(f"category_products:{product.category_id}")
# Varnish (HTTP PURGE)
purge_varnish(f"/products/{product_id}")
purge_varnish(f"/categories/{product.category_id}")
# CDN (Cloudflare Cache Tag)
cloudflare_purge_by_tag(f"product-{product_id}")
cloudflare_purge_by_tag(f"category-{product.category_id}")
Cloudflare Cache Tags (requires Enterprise or Cache Rules):
requests.post(
f"https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache",
headers={"Authorization": f"Bearer {token}"},
json={"tags": [f"product-{product_id}"]}
)
Monitoring Hit Rate by Level
# Varnish
varnish_main_cache_hit / (varnish_main_cache_hit + varnish_main_cache_miss)
# Redis
redis_keyspace_hits_total / (redis_keyspace_hits_total + redis_keyspace_misses_total)
# CDN: via Cloudflare Analytics API / CloudFront metrics
Target metrics:
- Browser cache hit rate: >60% for static assets
- CDN hit rate: >80% for public pages
- Varnish hit rate: >70% for HTML
- Redis hit rate: >85% for data
Timeline
Setting up full stack Browser→CDN→Varnish→Redis — 4–7 business days. Includes invalidation setup, monitoring, and load testing.







