🌍 Geographic Latency
CDN reduces latency by caching content closer to users worldwide. 10-30x faster!
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.
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.
example.com/image.jpg)Content that doesn’t change:
Why static content is perfect:
Content that changes, but can still be cached:
Considerations:
Content that shouldn’t be cached:
Design your APIs to work well with CDN caching:
Cache-Control header controls caching behavior:
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 responseimport org.springframework.http.HttpHeaders;import org.springframework.http.ResponseEntity;
@RestControllerpublic class ApiController {
@GetMapping("/products") public ResponseEntity<List<Product>> getProducts() { List<Product> products = fetchProducts();
return ResponseEntity.ok() .header(HttpHeaders.CACHE_CONTROL, "public, max-age=300") .header(HttpHeaders.ETAG, generateETag(products)) .body(products); }
@GetMapping("/user/profile") public ResponseEntity<UserProfile> getUserProfile() { UserProfile profile = fetchUserProfile();
return ResponseEntity.ok() .header(HttpHeaders.CACHE_CONTROL, "private, no-cache") .body(profile); }
@GetMapping("/static/image.jpg") public ResponseEntity<Resource> getImage() { // Static content - cache for 1 year Resource image = new ClassPathResource("image.jpg"); return ResponseEntity.ok() .header(HttpHeaders.CACHE_CONTROL, "public, max-age=31536000") .body(image); }}| Directive | Meaning | Example |
|---|---|---|
public | CDN can cache | public, max-age=3600 |
private | Only browser can cache | private, max-age=3600 |
no-cache | Revalidate before use | no-cache |
no-store | Don’t cache at all | no-store |
max-age=N | Cache for N seconds | max-age=300 (5 min) |
must-revalidate | Must revalidate when expired | must-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:
app.v1.2.4.js)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> """@Controllerpublic class WebController {
@GetMapping("/") public String index(Model model) { String version = "1.2.3"; model.addAttribute("cssVersion", version); model.addAttribute("jsVersion", version); return "index"; }}
// In template (Thymeleaf example)// <link rel="stylesheet" th:href="@{/static/styles.v${cssVersion}.css}">// <script th:src="@{/static/app.v${jsVersion}.js}"></script>Split your API into cacheable and non-cacheable endpoints:
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 ETagBenefits:
Simplest pattern - CDN only for static files:
When to use:
CDN caches API responses too:
When to use:
At the code level, design your services to be cache-friendly:
from abc import ABC, abstractmethodfrom typing import Optionalfrom 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' } }import java.util.List;import java.util.Optional;
interface ProductService { List<Product> getProducts();}
class DatabaseProductService implements ProductService { public List<Product> getProducts() { // Fetch from database return db.query("SELECT * FROM products"); }}
class CacheableProductService implements ProductService { private final ProductService dbService; private final CacheClient cache; private final String cacheKey = "products:all"; private final int ttl = 300; // 5 minutes
public CacheableProductService(ProductService dbService, CacheClient cache) { this.dbService = dbService; this.cache = cache; }
public List<Product> getProducts() { // Try cache first Optional<String> cached = cache.get(cacheKey); if (cached.isPresent()) { return deserialize(cached.get()); }
// Cache miss - fetch from DB List<Product> products = dbService.getProducts();
// Cache it cache.set(cacheKey, serialize(products), ttl);
return products; }
public void invalidateCache() { // Call this when products are updated cache.delete(cacheKey); }}
class HTTPResponseBuilder { public static Map<String, Object> buildCacheableResponse(Object data, int ttl) { Map<String, Object> response = new HashMap<>(); response.put("data", data);
Map<String, String> headers = new HashMap<>(); headers.put("Cache-Control", "public, max-age=" + ttl); headers.put("ETag", generateETag(data)); response.put("headers", headers);
return response; }
public static Map<String, Object> buildPrivateResponse(Object data) { Map<String, Object> response = new HashMap<>(); response.put("data", data);
Map<String, String> headers = new HashMap<>(); headers.put("Cache-Control", "private, no-cache"); response.put("headers", headers);
return response; }}| Provider | Best For | Key Features |
|---|---|---|
| Cloudflare | General purpose | Free tier, DDoS protection, global network |
| AWS CloudFront | AWS ecosystem | Integrated with S3, Lambda@Edge |
| Fastly | Dynamic content | Real-time purging, edge computing |
| Akamai | Enterprise | Largest 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.