Skip to content
Low Level Design Mastery Logo
LowLevelDesign Mastery

CDN & Edge Caching

Bringing content closer to users worldwide

Imagine a user in Tokyo trying to load a website hosted in New York. Every request travels 10,000+ kilometers, adding 200-300ms of latency just from distance.

Diagram

The problem: Geographic distance creates unavoidable latency. Even with fast networks, physics limits speed of light.

The solution: CDN (Content Delivery Network) - cache content at edge locations worldwide, closer to users.


CDN = Network of distributed servers that cache content at edge locations close to users.

Diagram
  1. User requests content (e.g., example.com/image.jpg)
  2. DNS routes to nearest edge server
  3. Edge server checks cache
    • Hit: Return cached content (fast!)
    • Miss: Fetch from origin, cache it, return to user
  4. Subsequent requests served from edge (fast!)

Content that doesn’t change:

  • Images (JPG, PNG, SVG)
  • CSS files
  • JavaScript files
  • Fonts
  • Videos
  • HTML (if static)
Diagram

Why static content is perfect:

  • ✅ Same for all users
  • ✅ Rarely changes
  • ✅ Can cache for long time (days/weeks)
  • ✅ Large files (benefit from edge caching)

Content that changes, but can still be cached:

  • API responses (with appropriate TTL)
  • User profiles (public parts)
  • Product catalogs
  • News articles
  • Search results (with short TTL)
Diagram

Considerations:

  • ⚠️ Use appropriate TTL (shorter than static)
  • ⚠️ May need cache invalidation
  • ⚠️ Consider user-specific data carefully

Content that shouldn’t be cached:

  • Real-time data (stock prices, chat)
  • User-specific sensitive data (account balance)
  • Personalized content (shopping cart)
  • Authentication tokens
  • One-time use data (OTP codes)

Design your APIs to work well with CDN caching:

Cache-Control header controls caching behavior:

cache_headers.py
from flask import Flask, jsonify, make_response
app = Flask(__name__)
@app.route('/products')
def get_products():
products = fetch_products()
response = make_response(jsonify(products))
# Cache for 5 minutes (public = CDN can cache)
response.headers['Cache-Control'] = 'public, max-age=300'
# ETag for validation
response.headers['ETag'] = generate_etag(products)
return response
@app.route('/user/profile')
def get_user_profile():
profile = fetch_user_profile()
response = make_response(jsonify(profile))
# Don't cache (private, user-specific)
response.headers['Cache-Control'] = 'private, no-cache'
return response
@app.route('/static/image.jpg')
def get_image():
# Static content - cache for 1 year
response = make_response(send_file('image.jpg'))
response.headers['Cache-Control'] = 'public, max-age=31536000'
return response
DirectiveMeaningExample
publicCDN can cachepublic, max-age=3600
privateOnly browser can cacheprivate, max-age=3600
no-cacheRevalidate before useno-cache
no-storeDon’t cache at allno-store
max-age=NCache for N secondsmax-age=300 (5 min)
must-revalidateMust revalidate when expiredmust-revalidate

Add version to static assets to enable long caching:

❌ Bad: /static/app.js (can't cache long - might change)
✅ Good: /static/app.v1.2.3.js (can cache forever - new version = new URL)

How it works:

  1. Deploy new version → new URL (app.v1.2.4.js)
  2. Old URLs still cached (forever)
  3. New requests get new version
  4. No cache invalidation needed!
"versioned_assets.py
from flask import Flask, url_for
app = Flask(__name__)
# In your template
@app.route('/')
def index():
version = "1.2.3"
return f"""
<html>
<link rel="stylesheet" href="/static/styles.v{version}.css">
<script src="/static/app.v{version}.js"></script>
</html>
"""

Split your API into cacheable and non-cacheable endpoints:

Diagram

Example:

  • /products → Cacheable (same for all users)
  • /user/cart → Not cacheable (user-specific)
  • /public/profile/:id → Cacheable (public profile)
  • /user/profile → Not cacheable (private, authenticated)

ETag = hash of content. Allows “conditional requests”:

Request: GET /products
If-None-Match: "abc123"
Response: 304 Not Modified (if ETag matches)
or 200 OK with new ETag

Benefits:

  • Saves bandwidth (304 = no body)
  • Validates freshness
  • Works with CDN

Simplest pattern - CDN only for static files:

Diagram

When to use:

  • ✅ Simple setup
  • ✅ Static website
  • ✅ API doesn’t need caching

CDN caches API responses too:

Diagram

When to use:

  • ✅ Global user base
  • ✅ API responses cacheable
  • ✅ Need low latency worldwide

LLD Connection: Designing Cache-Friendly Services

Section titled “LLD Connection: Designing Cache-Friendly Services”

At the code level, design your services to be cache-friendly:

"cache_friendly_service.py
from abc import ABC, abstractmethod
from typing import Optional
from datetime import datetime, timedelta
class ProductService(ABC):
@abstractmethod
def get_products(self) -> list:
pass
class DatabaseProductService(ProductService):
def get_products(self) -> list:
# Fetch from database
return db.query("SELECT * FROM products")
class CacheableProductService(ProductService):
def __init__(self, db_service: ProductService, cache: CacheClient):
self.db_service = db_service
self.cache = cache
self.cache_key = "products:all"
self.ttl = 300 # 5 minutes
def get_products(self) -> list:
# Try cache first
cached = self.cache.get(self.cache_key)
if cached:
return cached
# Cache miss - fetch from DB
products = self.db_service.get_products()
# Cache it
self.cache.set(self.cache_key, products, ttl=self.ttl)
return products
def invalidate_cache(self):
"""Call this when products are updated"""
self.cache.delete(self.cache_key)
class HTTPResponseBuilder:
@staticmethod
def build_cacheable_response(data, ttl: int = 300):
"""Build HTTP response with proper cache headers"""
return {
'data': data,
'headers': {
'Cache-Control': f'public, max-age={ttl}',
'ETag': generate_etag(data)
}
}
@staticmethod
def build_private_response(data):
"""Build HTTP response that shouldn't be cached"""
return {
'data': data,
'headers': {
'Cache-Control': 'private, no-cache'
}
}

ProviderBest ForKey Features
CloudflareGeneral purposeFree tier, DDoS protection, global network
AWS CloudFrontAWS ecosystemIntegrated with S3, Lambda@Edge
FastlyDynamic contentReal-time purging, edge computing
AkamaiEnterpriseLargest network, advanced features

🌍 Geographic Latency

CDN reduces latency by caching content closer to users worldwide. 10-30x faster!

📦 Static First

Static content (images, CSS, JS) is perfect for CDN. Cache for long time.

🔧 Cache-Friendly APIs

Use proper HTTP headers, version URLs, separate cacheable from non-cacheable.

🏷️ Version URLs

Version static assets (app.v1.2.3.js) to enable long caching without invalidation.