CDN and Edge Caching
CDN and Edge Caching
Content Delivery Networks (CDNs) cache content at edge locations worldwide, serving users from the nearest server. This dramatically reduces latency and improves page load times globally.
What is a CDN?
A CDN is a distributed network of servers that cache and deliver content from locations geographically closer to users. Instead of every request traveling to your origin server, the CDN serves cached content from edge servers.
How CDNs Work
When a user requests content, the CDN edge server checks its cache. If the content exists and is fresh, it's served immediately. If not, the CDN fetches from origin, caches it, and serves it.
User Request Flow:\n1. User requests https://cdn.example.com/logo.png\n2. DNS resolves to nearest CDN edge server\n3. Edge server checks cache\n4. If cached: serve immediately (cache HIT)\n5. If not cached: fetch from origin, cache, serve (cache MISS)\n6. Subsequent requests: served from cache
Popular CDN Providers
Major CDN providers offer global edge networks with different features and pricing models.
Cloudflare CDN Setup
Cloudflare provides free CDN services with automatic caching of static assets.
// Cloudflare automatically caches:\n// - Images (.jpg, .png, .gif, .webp)\n// - CSS and JavaScript files\n// - Fonts (.woff, .woff2, .ttf)\n// - Videos and audio files\n\n// Custom cache rules via Page Rules:\n// Cache Level: Standard, Aggressive, or Bypass\n// Edge Cache TTL: Override origin cache headers\n// Browser Cache TTL: Control client-side caching\n\n// Example Page Rule:\n// URL: *.example.com/assets/*\n// Settings:\n// Cache Level: Cache Everything\n// Edge Cache TTL: 1 month\n// Browser Cache TTL: 4 hours
AWS CloudFront Setup
CloudFront integrates with AWS services and provides advanced caching controls.
// CloudFront distribution configuration\n{\n "Origins": [{\n "DomainName": "origin.example.com",\n "Id": "myOrigin"\n }],\n "DefaultCacheBehavior": {\n "TargetOriginId": "myOrigin",\n "ViewerProtocolPolicy": "redirect-to-https",\n "AllowedMethods": ["GET", "HEAD", "OPTIONS"],\n "CachedMethods": ["GET", "HEAD"],\n "ForwardedValues": {\n "QueryString": false,\n "Cookies": { "Forward": "none" }\n },\n "MinTTL": 0,\n "DefaultTTL": 86400, // 1 day\n "MaxTTL": 31536000 // 1 year\n }\n}Cache Headers for CDN
HTTP cache headers control how CDNs and browsers cache content. Set appropriate headers on your origin server.
// Express.js cache headers\napp.use('/assets', (req, res, next) => {\n // Cache static assets for 1 year\n res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');\n next();\n});\n\napp.use('/api', (req, res, next) => {\n // Don't cache API responses\n res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');\n next();\n});\n\n// Vary header for content negotiation\nres.setHeader('Vary', 'Accept-Encoding, Accept-Language');\n\n// ETag for validation\nres.setHeader('ETag', generateETag(content));Cache-Control Directives
Understanding Cache-Control directives is crucial for proper CDN caching.
// Cache-Control directives:\n\n// public - Can be cached by CDN and browsers\nCache-Control: public, max-age=3600\n\n// private - Only browser cache, not CDN\nCache-Control: private, max-age=3600\n\n// no-cache - Must revalidate before serving\nCache-Control: no-cache\n\n// no-store - Never cache (sensitive data)\nCache-Control: no-store\n\n// immutable - Content never changes (versioned assets)\nCache-Control: public, max-age=31536000, immutable\n\n// s-maxage - CDN cache TTL (overrides max-age for CDN)\nCache-Control: public, max-age=3600, s-maxage=86400
Origin Shielding
Origin shielding adds an extra caching layer between edge servers and origin to reduce origin load.
// Without origin shielding:\nEdge Server 1 (cache miss) → Origin\nEdge Server 2 (cache miss) → Origin\nEdge Server 3 (cache miss) → Origin\n// 3 origin requests for same content\n\n// With origin shielding:\nEdge Server 1 (cache miss) → Shield Server (cache miss) → Origin\nEdge Server 2 (cache miss) → Shield Server (cache hit)\nEdge Server 3 (cache miss) → Shield Server (cache hit)\n// Only 1 origin request\n\n// CloudFront origin shield configuration:\n{\n "OriginShield": {\n "Enabled": true,\n "OriginShieldRegion": "us-east-1"\n }\n}Purging CDN Cache
Invalidate cached content when you deploy updates or need to remove stale data.
// Cloudflare API purge\nconst cloudflare = require('cloudflare')({\n token: 'your-api-token'\n});\n\n// Purge specific files\nawait cloudflare.zones.purgeCache(zoneId, {\n files: [\n 'https://example.com/style.css',\n 'https://example.com/app.js'\n ]\n});\n\n// Purge by cache tag\nawait cloudflare.zones.purgeCache(zoneId, {\n tags: ['product-images']\n});\n\n// Purge everything (use sparingly!)\nawait cloudflare.zones.purgeCache(zoneId, {\n purge_everything: true\n});// AWS CloudFront invalidation\nconst AWS = require('aws-sdk');\nconst cloudfront = new AWS.CloudFront();\n\nconst params = {\n DistributionId: 'E1234567890ABC',\n InvalidationBatch: {\n CallerReference: Date.now().toString(),\n Paths: {\n Quantity: 2,\n Items: [\n '/assets/style.css',\n '/assets/app.js'\n ]\n }\n }\n};\n\nawait cloudfront.createInvalidation(params).promise();Static vs Dynamic Caching
Different content types require different caching strategies.
// Static content (images, CSS, JS):\n// - Long cache times (1 month to 1 year)\n// - Use versioned filenames (app.v123.js)\n// - Cache-Control: public, max-age=31536000, immutable\napp.use('/static', express.static('public', {\n maxAge: '1y',\n immutable: true\n}));\n\n// Dynamic content (HTML pages):\n// - Short cache times (5 minutes to 1 hour)\n// - Use ETag for validation\n// - Cache-Control: public, max-age=300, must-revalidate\napp.get('/blog/:slug', async (req, res) => {\n const post = await getPost(req.params.slug);\n const etag = generateETag(post);\n \n if (req.get('If-None-Match') === etag) {\n return res.status(304).end();\n }\n \n res.set({\n 'Cache-Control': 'public, max-age=300, must-revalidate',\n 'ETag': etag\n });\n res.render('post', { post });\n});Edge Computing with Cloudflare Workers
Run code at the edge to customize caching behavior and content delivery.
// Cloudflare Worker example\naddEventListener('fetch', event => {\n event.respondWith(handleRequest(event.request));\n});\n\nasync function handleRequest(request) {\n const url = new URL(request.url);\n \n // Custom caching logic\n if (url.pathname.startsWith('/api/')) {\n // Don't cache API\n return fetch(request);\n }\n \n // Check cache\n const cache = caches.default;\n let response = await cache.match(request);\n \n if (!response) {\n // Fetch from origin\n response = await fetch(request);\n \n // Cache for 1 hour\n const headers = new Headers(response.headers);\n headers.set('Cache-Control', 'public, max-age=3600');\n \n response = new Response(response.body, {\n status: response.status,\n headers\n });\n \n event.waitUntil(cache.put(request, response.clone()));\n }\n \n return response;\n}Monitoring CDN Performance
Track cache hit rates, bandwidth usage, and performance metrics.
// Key CDN metrics:\n\n// Cache Hit Rate: (cache hits / total requests) * 100\n// Target: >90% for static assets, >70% for dynamic content\n\n// Origin Shield Hit Rate: Shield cache hits / shield requests\n// Target: >80%\n\n// Bandwidth Saved: Origin bandwidth vs total bandwidth\n// Good CDN should save 60-90% bandwidth\n\n// P95 Response Time: 95th percentile request latency\n// Target: <100ms globally\n\n// Cache Miss Rate: Failed cache lookups\n// High miss rate indicates caching issues