Module 16: CDN and Edge Computing

What is a CDN?

A Content Delivery Network (CDN) is a geographically distributed network of servers that delivers content to users from the nearest location.
┌─────────────────────────────────────────────────────────────────┐ │ CDN OVERVIEW │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ WITHOUT CDN: │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ User (Tokyo) ─────────── 150ms ─────────► Origin │ │ │ │ (New York) │ │ │ │ User (London) ────────── 80ms ─────────► │ │ │ │ │ │ │ │ User (Sydney) ────────── 200ms ─────────► │ │ │ │ │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ WITH CDN: │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ User (Tokyo) ──── 10ms ───► Edge (Tokyo) │ │ │ │ │ │ │ │ │ User (London) ── 15ms ──► Edge (London) │ │ │ │ │ (cache miss) │ │ │ │ User (Sydney) ── 12ms ──► Edge (Sydney) │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ Origin (New York) │ │ │ │ │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ Benefits: │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ • Lower latency (closer to users) │ │ │ │ • Reduced origin load │ │ │ │ • DDoS protection │ │ │ │ • Global availability │ │ │ │ • SSL/TLS termination at edge │ │ │ │ • Bandwidth cost savings │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘

CDN Architecture

┌─────────────────────────────────────────────────────────────────┐ │ CDN ARCHITECTURE │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ │ │ │ DNS (GeoDNS) │ │ │ └────────┬────────┘ │ │ │ │ │ ┌─────────────────┼─────────────────┐ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Edge │ │ Edge │ │ Edge │ │ │ │ PoP 1 │ │ PoP 2 │ │ PoP 3 │ │ │ │ (London) │ │ (Tokyo) │ │ (Sydney) │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ │ │ │ │ │ (Cache Miss - fetch from origin) │ │ │ │ │ │ │ └────────────────┼────────────────┘ │ │ │ │ │ ┌──────▼──────┐ │ │ │ Shield │ (Optional mid-tier cache) │ │ │ PoP │ │ │ └──────┬──────┘ │ │ │ │ │ ┌──────▼──────┐ │ │ │ Origin │ │ │ │ Server │ │ │ └─────────────┘ │ │ │ │ Components: │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ PoP (Point of Presence): Edge server location │ │ │ │ Edge Server: Caches and serves content │ │ │ │ Shield: Mid-tier cache protecting origin │ │ │ │ Origin: Source of truth for content │ │ │ │ GeoDNS: Routes users to nearest PoP │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘

Content Types and Caching

┌─────────────────────────────────────────────────────────────────┐ │ WHAT TO CACHE ON CDN │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ STATIC CONTENT (Always cache): │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ • Images (JPEG, PNG, WebP, SVG) │ │ │ │ • CSS files │ │ │ │ • JavaScript files │ │ │ │ • Fonts (WOFF, WOFF2) │ │ │ │ • Videos (MP4, WebM) │ │ │ │ • Static HTML │ │ │ │ │ │ │ │ Cache-Control: public, max-age=31536000, immutable │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ DYNAMIC CONTENT (Cache with care): │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ • API responses (product listings, search results) │ │ │ │ • Personalized pages (can use Edge Side Includes) │ │ │ │ • HTML pages │ │ │ │ │ │ │ │ Cache-Control: public, max-age=60, s-maxage=300 │ │ │ │ (Browser: 1 min, CDN: 5 min) │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ NEVER CACHE: │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ • User-specific data (cart, profile) │ │ │ │ • Authentication endpoints │ │ │ │ • POST/PUT/DELETE requests │ │ │ │ • Real-time data │ │ │ │ │ │ │ │ Cache-Control: private, no-store │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘

Cache-Control Headers

┌─────────────────────────────────────────────────────────────────┐ │ CACHE-CONTROL HEADERS │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Directives: │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ public : Can be cached by CDN and browsers │ │ │ │ private : Only browser cache, not CDN │ │ │ │ no-cache : Must revalidate before using cached │ │ │ │ no-store : Don't cache at all │ │ │ │ max-age=N : Cache for N seconds │ │ │ │ s-maxage=N : CDN cache for N seconds (overrides) │ │ │ │ immutable : Content will never change │ │ │ │ stale-while-revalidate=N : Serve stale while updating │ │ │ │ stale-if-error=N : Serve stale if origin fails │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ Common Patterns: │ │ │ │ Versioned static files (app.v1.2.3.js): │ │ Cache-Control: public, max-age=31536000, immutable │ │ │ │ Frequently updated content: │ │ Cache-Control: public, max-age=0, s-maxage=60, │ │ stale-while-revalidate=300 │ │ │ │ User-specific content: │ │ Cache-Control: private, max-age=0, no-store │ │ │ │ API responses (list endpoints): │ │ Cache-Control: public, max-age=60, │ │ stale-if-error=300 │ │ │ └─────────────────────────────────────────────────────────────────┘

Setting Cache Headers in Go

go
package main import ( "fmt" "net/http" "path/filepath" "strings" "time" ) // CacheControl generates Cache-Control header value type CacheControl struct { Public bool Private bool MaxAge time.Duration SMaxAge time.Duration NoCache bool NoStore bool Immutable bool StaleWhileRevalidate time.Duration StaleIfError time.Duration } func (c CacheControl) String() string { var parts []string if c.NoStore { return "no-store" } if c.NoCache { parts = append(parts, "no-cache") } if c.Public { parts = append(parts, "public") } else if c.Private { parts = append(parts, "private") } if c.MaxAge > 0 { parts = append(parts, fmt.Sprintf("max-age=%d", int(c.MaxAge.Seconds()))) } if c.SMaxAge > 0 { parts = append(parts, fmt.Sprintf("s-maxage=%d", int(c.SMaxAge.Seconds()))) } if c.Immutable { parts = append(parts, "immutable") } if c.StaleWhileRevalidate > 0 { parts = append(parts, fmt.Sprintf("stale-while-revalidate=%d", int(c.StaleWhileRevalidate.Seconds()))) } if c.StaleIfError > 0 { parts = append(parts, fmt.Sprintf("stale-if-error=%d", int(c.StaleIfError.Seconds()))) } return strings.Join(parts, ", ") } // Predefined cache policies var ( // Immutable static assets (hashed filenames) ImmutableAssets = CacheControl{ Public: true, MaxAge: 365 * 24 * time.Hour, Immutable: true, } // Short-lived dynamic content DynamicContent = CacheControl{ Public: true, MaxAge: 0, SMaxAge: 60 * time.Second, StaleWhileRevalidate: 5 * time.Minute, } // API list endpoints APIListResponse = CacheControl{ Public: true, MaxAge: 60 * time.Second, StaleIfError: 5 * time.Minute, } // Private user data PrivateUserData = CacheControl{ Private: true, NoStore: true, } ) // CacheMiddleware sets appropriate cache headers func CacheMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Determine cache policy based on path cc := getCachePolicy(r.URL.Path) w.Header().Set("Cache-Control", cc.String()) // Add Vary header for content negotiation w.Header().Set("Vary", "Accept-Encoding") next.ServeHTTP(w, r) }) } func getCachePolicy(path string) CacheControl { ext := filepath.Ext(path) // Static assets with hash in filename if isHashedAsset(path) { return ImmutableAssets } // Static assets without hash switch ext { case ".js", ".css", ".woff", ".woff2": return CacheControl{ Public: true, MaxAge: 24 * time.Hour, } case ".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg": return CacheControl{ Public: true, MaxAge: 7 * 24 * time.Hour, } } // API endpoints if strings.HasPrefix(path, "/api/") { if strings.Contains(path, "/user/") || strings.Contains(path, "/cart/") { return PrivateUserData } return APIListResponse } // HTML pages return DynamicContent } func isHashedAsset(path string) bool { // Match patterns like: app.abc123.js, style.def456.css parts := strings.Split(filepath.Base(path), ".") if len(parts) >= 3 { hash := parts[len(parts)-2] return len(hash) >= 6 && len(hash) <= 32 } return false } // Handler example with custom caching func ProductsHandler(w http.ResponseWriter, r *http.Request) { // Set cache headers for product listing w.Header().Set("Cache-Control", "public, max-age=60, s-maxage=300, stale-while-revalidate=600") w.Header().Set("Vary", "Accept-Encoding, Accept-Language") // Set ETag for conditional requests etag := generateETag(getProductsLastModified()) w.Header().Set("ETag", etag) // Check If-None-Match if r.Header.Get("If-None-Match") == etag { w.WriteHeader(http.StatusNotModified) return } // Return products w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"products": [...]}`)) } func generateETag(t time.Time) string { return fmt.Sprintf(`"%d"`, t.UnixNano()) } func getProductsLastModified() time.Time { return time.Now() // In practice, track actual modification time }

CDN Cache Invalidation

┌─────────────────────────────────────────────────────────────────┐ │ CDN CACHE INVALIDATION │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Methods: │ │ │ │ 1. PURGE (Immediate invalidation) │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ DELETE /purge/path/to/file │ │ │ │ │ │ │ │ Pros: Immediate effect │ │ │ │ Cons: Rate limits, costs, propagation delay │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ 2. VERSION/HASH IN URL │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ /static/app.v1.2.3.js → /static/app.v1.2.4.js │ │ │ │ /images/logo.abc123.png │ │ │ │ │ │ │ │ Pros: No purge needed, immutable caching │ │ │ │ Cons: Requires build process, HTML must update │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ 3. SHORT TTL + REVALIDATION │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Cache-Control: max-age=60, stale-while-revalidate=300 │ │ │ │ │ │ │ │ CDN serves stale while fetching fresh content │ │ │ │ │ │ │ │ Pros: Always fresh within TTL, graceful updates │ │ │ │ Cons: More origin requests │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ 4. CACHE TAGS (Surrogate Keys) │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Response: Surrogate-Key: product-123 category-shoes │ │ │ │ Purge: POST /purge {"tags": ["product-123"]} │ │ │ │ │ │ │ │ Pros: Purge related content together │ │ │ │ Cons: CDN-specific, complexity │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘

CDN Purge Implementation

go
package cdn import ( "bytes" "context" "encoding/json" "fmt" "net/http" "time" ) // CDNPurger handles cache invalidation type CDNPurger interface { PurgeURL(ctx context.Context, url string) error PurgeURLs(ctx context.Context, urls []string) error PurgeTags(ctx context.Context, tags []string) error PurgeAll(ctx context.Context) error } // CloudflarePurger implements CDN purging for Cloudflare type CloudflarePurger struct { zoneID string apiToken string client *http.Client } func NewCloudflarePurger(zoneID, apiToken string) *CloudflarePurger { return &CloudflarePurger{ zoneID: zoneID, apiToken: apiToken, client: &http.Client{Timeout: 30 * time.Second}, } } func (c *CloudflarePurger) PurgeURLs(ctx context.Context, urls []string) error { payload := map[string][]string{"files": urls} return c.purge(ctx, payload) } func (c *CloudflarePurger) PurgeTags(ctx context.Context, tags []string) error { payload := map[string][]string{"tags": tags} return c.purge(ctx, payload) } func (c *CloudflarePurger) PurgeAll(ctx context.Context) error { payload := map[string]bool{"purge_everything": true} return c.purge(ctx, payload) } func (c *CloudflarePurger) purge(ctx context.Context, payload interface{}) error { url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/purge_cache", c.zoneID) body, _ := json.Marshal(payload) req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(body)) if err != nil { return err } req.Header.Set("Authorization", "Bearer "+c.apiToken) req.Header.Set("Content-Type", "application/json") resp, err := c.client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("purge failed: %d", resp.StatusCode) } return nil } // FastlyPurger implements CDN purging for Fastly type FastlyPurger struct { serviceID string apiKey string client *http.Client } func NewFastlyPurger(serviceID, apiKey string) *FastlyPurger { return &FastlyPurger{ serviceID: serviceID, apiKey: apiKey, client: &http.Client{Timeout: 30 * time.Second}, } } func (f *FastlyPurger) PurgeURL(ctx context.Context, url string) error { req, err := http.NewRequestWithContext(ctx, "PURGE", url, nil) if err != nil { return err } req.Header.Set("Fastly-Key", f.apiKey) resp, err := f.client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("purge failed: %d", resp.StatusCode) } return nil } func (f *FastlyPurger) PurgeTags(ctx context.Context, tags []string) error { // Fastly uses surrogate keys for _, tag := range tags { url := fmt.Sprintf("https://api.fastly.com/service/%s/purge/%s", f.serviceID, tag) req, err := http.NewRequestWithContext(ctx, "POST", url, nil) if err != nil { return err } req.Header.Set("Fastly-Key", f.apiKey) resp, err := f.client.Do(req) if err != nil { return err } resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("purge tag %s failed: %d", tag, resp.StatusCode) } } return nil } // Usage with product updates type ProductService struct { db Database cdn CDNPurger cache Cache } func (s *ProductService) UpdateProduct(ctx context.Context, product *Product) error { // Update in database if err := s.db.UpdateProduct(ctx, product); err != nil { return err } // Invalidate caches go func() { bgCtx := context.Background() // Local/Redis cache s.cache.Delete(bgCtx, fmt.Sprintf("product:%d", product.ID)) // CDN cache urls := []string{ fmt.Sprintf("https://example.com/products/%d", product.ID), fmt.Sprintf("https://example.com/api/products/%d", product.ID), } s.cdn.PurgeURLs(bgCtx, urls) // Or use tags s.cdn.PurgeTags(bgCtx, []string{ fmt.Sprintf("product-%d", product.ID), fmt.Sprintf("category-%d", product.CategoryID), }) }() return nil }

Edge Computing

┌─────────────────────────────────────────────────────────────────┐ │ EDGE COMPUTING │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Beyond caching: Run code at the edge │ │ │ │ Traditional CDN: │ │ Request → Edge (cache) → Origin │ │ │ │ Edge Computing: │ │ Request → Edge (compute + cache) → Origin (maybe) │ │ │ │ Use Cases: │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ 1. A/B Testing │ │ │ │ Route users to different experiences at edge │ │ │ │ │ │ │ │ 2. Authentication/Authorization │ │ │ │ Validate JWT at edge, reject unauthorized │ │ │ │ │ │ │ │ 3. Geolocation │ │ │ │ Personalize content based on user location │ │ │ │ │ │ │ │ 4. Bot Detection │ │ │ │ Block malicious traffic before it reaches origin │ │ │ │ │ │ │ │ 5. Request/Response Transformation │ │ │ │ Modify headers, rewrite URLs, transform content │ │ │ │ │ │ │ │ 6. API Gateway │ │ │ │ Rate limiting, request routing, authentication │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ Providers: │ │ • Cloudflare Workers │ │ • AWS Lambda@Edge / CloudFront Functions │ │ • Fastly Compute@Edge │ │ • Vercel Edge Functions │ │ • Deno Deploy │ │ │ └─────────────────────────────────────────────────────────────────┘

Edge Function Examples

go
// Cloudflare Workers example (JavaScript, but concepts apply) // This shows typical edge computing patterns package edge import ( "context" "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/json" "net/http" "strings" "time" ) // EdgeHandler represents an edge function type EdgeHandler interface { Handle(ctx context.Context, req *http.Request) (*http.Response, error) } // ABTestHandler routes users to different variants type ABTestHandler struct { variants []string cookieName string distribution []float64 // e.g., [0.5, 0.5] for 50/50 } func (h *ABTestHandler) Handle(ctx context.Context, req *http.Request) (*http.Response, error) { // Check existing assignment cookie, _ := req.Cookie(h.cookieName) var variant string if cookie != nil { variant = cookie.Value } else { // Assign new variant based on random hash variant = h.assignVariant(req) } // Modify request to route to variant if variant == "B" { req.URL.Path = "/b" + req.URL.Path } // Add variant header for origin req.Header.Set("X-Variant", variant) // Return modified request (in real edge, this would forward) return nil, nil } func (h *ABTestHandler) assignVariant(req *http.Request) string { // Use client IP + user agent for consistent assignment key := req.RemoteAddr + req.UserAgent() hash := sha256.Sum256([]byte(key)) value := float64(hash[0]) / 256.0 cumulative := 0.0 for i, prob := range h.distribution { cumulative += prob if value < cumulative { return h.variants[i] } } return h.variants[len(h.variants)-1] } // JWTAuthHandler validates JWT tokens at the edge type JWTAuthHandler struct { publicKey []byte origin http.Handler } func (h *JWTAuthHandler) Handle(ctx context.Context, req *http.Request) (*http.Response, error) { // Extract token from Authorization header auth := req.Header.Get("Authorization") if !strings.HasPrefix(auth, "Bearer ") { return unauthorizedResponse(), nil } token := strings.TrimPrefix(auth, "Bearer ") // Validate JWT (simplified) claims, err := h.validateJWT(token) if err != nil { return unauthorizedResponse(), nil } // Add user info to request for origin req.Header.Set("X-User-ID", claims.UserID) req.Header.Set("X-User-Role", claims.Role) // Continue to origin return nil, nil } type JWTClaims struct { UserID string `json:"sub"` Role string `json:"role"` Exp int64 `json:"exp"` } func (h *JWTAuthHandler) validateJWT(token string) (*JWTClaims, error) { parts := strings.Split(token, ".") if len(parts) != 3 { return nil, fmt.Errorf("invalid token format") } // Decode payload payload, err := base64.RawURLEncoding.DecodeString(parts[1]) if err != nil { return nil, err } var claims JWTClaims if err := json.Unmarshal(payload, &claims); err != nil { return nil, err } // Check expiration if claims.Exp < time.Now().Unix() { return nil, fmt.Errorf("token expired") } // Verify signature (simplified - use proper JWT library) // ... return &claims, nil } func unauthorizedResponse() *http.Response { return &http.Response{ StatusCode: http.StatusUnauthorized, Body: io.NopCloser(strings.NewReader("Unauthorized")), } } // GeoRoutingHandler routes based on user location type GeoRoutingHandler struct { regionOrigins map[string]string // region -> origin URL defaultOrigin string } func (h *GeoRoutingHandler) Handle(ctx context.Context, req *http.Request) (*http.Response, error) { // Get user's country from CDN headers (provided by edge) country := req.Header.Get("CF-IPCountry") // Cloudflare if country == "" { country = req.Header.Get("X-Country-Code") // Generic } // Map country to region region := h.countryToRegion(country) // Get origin for region origin, ok := h.regionOrigins[region] if !ok { origin = h.defaultOrigin } // Rewrite request to appropriate origin req.URL.Host = origin req.Header.Set("X-User-Country", country) req.Header.Set("X-User-Region", region) return nil, nil } func (h *GeoRoutingHandler) countryToRegion(country string) string { regions := map[string][]string{ "us": {"US", "CA", "MX"}, "eu": {"GB", "DE", "FR", "IT", "ES", "NL"}, "apac": {"JP", "KR", "AU", "SG", "IN"}, } for region, countries := range regions { for _, c := range countries { if c == country { return region } } } return "default" } // RateLimitHandler implements edge rate limiting type RateLimitHandler struct { store KVStore // Edge KV store (like Cloudflare KV) limit int windowSeconds int } type KVStore interface { Get(ctx context.Context, key string) (string, error) Put(ctx context.Context, key string, value string, ttlSeconds int) error Incr(ctx context.Context, key string) (int, error) } func (h *RateLimitHandler) Handle(ctx context.Context, req *http.Request) (*http.Response, error) { // Create rate limit key based on IP or API key key := fmt.Sprintf("ratelimit:%s", req.RemoteAddr) // Increment counter count, err := h.store.Incr(ctx, key) if err != nil { // On error, allow request return nil, nil } // Set TTL on first request if count == 1 { h.store.Put(ctx, key, "1", h.windowSeconds) } // Check limit if count > h.limit { return &http.Response{ StatusCode: http.StatusTooManyRequests, Header: http.Header{ "Retry-After": []string{fmt.Sprintf("%d", h.windowSeconds)}, }, Body: io.NopCloser(strings.NewReader("Rate limit exceeded")), }, nil } // Add headers for client req.Header.Set("X-RateLimit-Limit", fmt.Sprintf("%d", h.limit)) req.Header.Set("X-RateLimit-Remaining", fmt.Sprintf("%d", h.limit-count)) return nil, nil }

CDN Best Practices

┌─────────────────────────────────────────────────────────────────┐ │ CDN BEST PRACTICES │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. USE VERSIONED ASSETS │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ ✓ /static/app.abc123.js (hash in filename) │ │ │ │ ✗ /static/app.js?v=123 (query string) │ │ │ │ │ │ │ │ Hash in filename → immutable → cache forever │ │ │ │ Query string may not be cached by all CDNs │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ 2. SET APPROPRIATE CACHE HEADERS │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Static assets: Cache-Control: public, max-age=1y │ │ │ │ HTML pages: Cache-Control: no-cache (or short TTL) │ │ │ │ API responses: Vary by appropriate headers │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ 3. USE STALE-WHILE-REVALIDATE │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Serve stale content while fetching fresh in bg │ │ │ │ Users always get fast response │ │ │ │ Content refreshes automatically │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ 4. IMPLEMENT CACHE TAGS │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Tag responses with logical identifiers │ │ │ │ Purge by tag when underlying data changes │ │ │ │ More efficient than URL-by-URL purging │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ 5. MONITOR CDN PERFORMANCE │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ • Cache hit ratio (target: >90%) │ │ │ │ • Origin request rate │ │ │ │ • Edge latency by region │ │ │ │ • Error rates (5xx from origin) │ │ │ │ • Bandwidth usage │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ 6. COMPRESS CONTENT │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Enable gzip/brotli compression │ │ │ │ Most CDNs handle this automatically │ │ │ │ Reduces bandwidth, improves load times │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘

Interview Questions

  1. How does a CDN improve performance?
    • Reduced latency (edge servers closer to users)
    • Reduced origin load (cached responses)
    • Better availability (multiple PoPs)
    • Bandwidth savings
  2. How do you handle cache invalidation with a CDN?
    • Versioned filenames for static assets
    • Purge API for immediate invalidation
    • Short TTL with stale-while-revalidate
    • Cache tags for grouped invalidation
  3. What's the difference between max-age and s-maxage?
    • max-age: Both browser and CDN caching
    • s-maxage: CDN only (overrides max-age for CDN)
    • Use different values for different caching needs
  4. When would you use edge computing vs origin?
    • Edge: Auth, A/B testing, geolocation, rate limiting
    • Origin: Complex business logic, database access
    • Edge is faster but has compute limits
  5. How do you handle personalized content with a CDN?
    • Edge Side Includes (ESI)
    • Client-side hydration
    • Cookie-based variant caching
    • Bypass cache for personalized sections

Summary

┌─────────────────────────────────────────────────────────────────┐ │ CDN & EDGE SUMMARY │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ CDN Benefits: │ │ • Reduced latency (edge proximity) │ │ • Reduced origin load (caching) │ │ • DDoS protection │ │ • Global availability │ │ │ │ Cache Strategy: │ │ • Static: Long TTL + immutable │ │ • Dynamic: Short TTL + stale-while-revalidate │ │ • Private: no-store │ │ │ │ Invalidation: │ │ • Best: Versioned filenames │ │ • Good: Cache tags with purge │ │ • OK: Short TTL with revalidation │ │ │ │ Edge Computing: │ │ • Auth/authz at edge │ │ • A/B testing without origin │ │ • Rate limiting at edge │ │ • Request transformation │ │ │ │ Key Insight: │ │ "Move computation closer to users. CDN caches content, │ │ edge computing runs your code at those same locations." │ │ │ └─────────────────────────────────────────────────────────────────┘

All Blogs
Tags:cdnedge-computinglatencyglobal-systems