Overview
A CDN (Content Delivery Network) is a geographically distributed network of servers (Points of Presence / PoPs) that cache content close to users. Primary benefits: reduced latency (serve from edge, not origin), reduced origin load, DDoS mitigation via absorption.
Architecture
WITHOUT CDN:
User (Tokyo) ──── 150ms RTT ────▶ Origin (US-East)
User (London) ─── 100ms RTT ───▶ Origin (US-East)
User (Sydney) ─── 200ms RTT ───▶ Origin (US-East)
All traffic hits origin → high latency, high cost
WITH CDN (Pull Model):
┌───────────────────────────────────────────────────────┐
│ Origin Server (US-East) │
│ (only serves cache misses) │
└───────────────────────────────────────────────────────┘
▲ ▲ ▲
│ cache miss │ cache miss │ cache miss
│ (1st request)│ │
┌───────┴──────┐ ┌─────┴──────┐ ┌───┴────────┐
│ PoP Tokyo │ │ PoP London │ │ PoP Sydney │
│ (edge node) │ │ │ │ │
│ Cached: │ │ Cached: │ │ Cached: │
│ /image.jpg │ │ /style.css│ │ /app.js │
└──────┬───────┘ └──────┬─────┘ └──────┬─────┘
│ │ │
User (Tokyo) User (London) User (Sydney)
5ms RTT 8ms RTT 6ms RTT
CDN REQUEST FLOW (Pull):
═══════════════════════════════════════════════════════
1. User requests https://cdn.example.com/image.jpg
2. DNS resolves to nearest PoP (Anycast routing)
3. PoP checks local cache:
HIT: return cached file (← 5ms)
MISS: forward to origin, cache response, return to user (← 150ms first time)
4. Subsequent requests for same file → served from edge
CDN CACHE INVALIDATION:
═══════════════════════════════════════════════════════
Problem: content updated at origin, but CDN still serves old version
Strategy 1: URL versioning (best)
/app.js?v=abc123 (content hash in filename/query)
New deploy changes hash → new URL → cache miss (intentional)
Old URL still cached (backward compatible)
Strategy 2: Purge API
POST https://api.cloudfront.net/invalidations
{ "paths": ["/image.jpg", "/style.css"] }
Takes 0-60s to propagate to all PoPs
Has cost (AWS charges per invalidation path)
Strategy 3: Short TTL
Cache-Control: max-age=60
Stale content for at most 60s
Simple but increases origin load
PUSH vs PULL:
═══════════════════════════════════════════════════════
Pull CDN:
- Origin is master, CDN fetches on demand
- Good for frequently-changing content
- First-request latency hits origin
- No storage cost management
Push CDN:
- You proactively push content to CDN nodes
- Good for large static assets (video, software downloads)
- Content available before first request
- You manage expiry and storage
Technical Implementation
CloudFront with S3 Origin (AWS CDN)
// AWS CDK: CloudFront distribution with S3 origin
import { Distribution, ViewerProtocolPolicy, CachePolicy } from 'aws-cdk-lib/aws-cloudfront';
import { S3Origin } from 'aws-cdk-lib/aws-cloudfront-origins';
const distribution = new Distribution(this, 'WebDistribution', {
defaultBehavior: {
origin: new S3Origin(assetsBucket),
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cachePolicy: new CachePolicy(this, 'AppCachePolicy', {
defaultTtl: Duration.days(365), // long TTL for versioned assets
minTtl: Duration.seconds(0),
maxTtl: Duration.days(365),
enableAcceptEncodingGzip: true,
enableAcceptEncodingBrotli: true,
}),
},
additionalBehaviors: {
'/api/*': {
origin: new HttpOrigin('api.example.com'),
cachePolicy: CachePolicy.CACHING_DISABLED, // never cache API responses
},
},
});
Cache-Control Headers
// Express: set cache headers per resource type
app.use('/static', (req, res, next) => {
// Versioned assets (hash in filename): cache forever
if (/\.[a-f0-9]{8,}\.(js|css|png|jpg)$/.test(req.path)) {
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
} else {
// Non-versioned: 1 hour
res.setHeader('Cache-Control', 'public, max-age=3600');
}
next();
}, express.static('dist'));
// HTML: never cache (so new deploys are picked up immediately)
app.get('/', (req, res) => {
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
res.sendFile('index.html');
});
// API responses: no CDN caching
app.use('/api', (req, res, next) => {
res.setHeader('Cache-Control', 'private, no-store');
next();
});
CDN Cache Invalidation (CloudFront)
import { CloudFrontClient, CreateInvalidationCommand } from '@aws-sdk/client-cloudfront';
const cloudfront = new CloudFrontClient({ region: 'us-east-1' });
export async function invalidatePaths(paths: string[]): Promise<void> {
await cloudfront.send(new CreateInvalidationCommand({
DistributionId: process.env.CLOUDFRONT_DISTRIBUTION_ID,
InvalidationBatch: {
CallerReference: Date.now().toString(), // unique per request
Paths: {
Quantity: paths.length,
Items: paths, // e.g., ['/index.html', '/api/products/*']
},
},
}));
}
// Call after deploy to invalidate HTML (JS/CSS use versioned URLs)
await invalidatePaths(['/index.html', '/sitemap.xml']);
Interview Preparation
Q: How does a CDN know which PoP to send a user to?
Anycast routing + GeoDNS. With Anycast, the same IP is announced from multiple PoPs — routers automatically route to the nearest one (BGP shortest path). With GeoDNS, the DNS server returns different IP addresses based on the user's geographic location. Most CDNs use a combination of both.
Q: What content should NOT be cached at CDN?
Personalized content (shopping cart, user profile), authenticated responses (anything behind Authorization header), real-time data (stock prices, live scores), POST/PUT/DELETE responses. Use Cache-Control: private, no-store for these.
Q: How do you deploy a frontend app update without users getting stale JavaScript?
Content-hash-based filenames. Webpack/Vite output app.abc123.js where the hash changes with every build. index.html references the new filename. CDN caches app.abc123.js forever (it never changes). On deploy, invalidate only index.html — it's tiny, cheap, and the new index.html references new hashed JS/CSS filenames.
Q: CDN vs Load Balancer — what's the difference?
CDN: caches and serves static/cacheable content from edge nodes close to users. Reduces latency by geographic proximity. Load balancer: distributes traffic across multiple backend servers in one datacenter. Reduces load per server and provides failover. Both can be in the same architecture: CDN → internet → Load Balancer → App Servers.