- Published on
CDN and Origin Architecture — Serving Globally Without a Global Database
- Authors

- Name
- Sanjeev Sharma
- @webcoderspeed1
Introduction
A Content Delivery Network amplifies origin capacity by caching content at edge locations worldwide. But CDNs only work when properly configured. Understanding cache validation, origin optimization, edge request manipulation, and failover patterns is the difference between 10x faster delivery and wasted money. Most teams deploy CDNs without understanding their topology.
- CDN as First Line of Defense
- Origin Optimization and Cache-Control
- Lambda@Edge for Request Manipulation
- Origin Shield and Cache Behavior
- Cache Invalidation Strategies
- Geolocation Routing and WAF
- CDN Failover and Health Checks
- Measuring CDN Hit Ratio
- Checklist
- Conclusion
CDN as First Line of Defense
CDNs sit between users and origin, absorbing request volume:
// Without CDN: Origin handles every request
// 1000 users -> 1000 requests to origin
// With CDN: Origin handles cache misses only
// 1000 users requesting same content -> 1 request to origin
// Hit ratio: 95% = origin handles only 50 requests
interface CDNMetrics {
totalRequests: number;
originRequests: number;
edgeResponses: number;
hitRatio: number;
bandwidthSaved: string;
}
function calculateCDNBenefit(
uniqueUsers: number,
requestsPerUser: number,
contentSize: number,
hitRatio: number
): CDNMetrics {
const totalRequests = uniqueUsers * requestsPerUser;
const originRequests = totalRequests * (1 - hitRatio);
const edgeResponses = totalRequests * hitRatio;
const bandwidthSaved = `${((edgeResponses * contentSize) / 1024 / 1024 / 1024).toFixed(2)} GB`;
return {
totalRequests,
originRequests,
edgeResponses,
hitRatio,
bandwidthSaved,
};
}
// Example: 100K users, 10 requests each, 1MB average
const metrics = calculateCDNBenefit(100000, 10, 1024 * 1024, 0.95);
// originRequests: 50,000 (vs 1M without CDN)
// bandwidthSaved: 950 GB (vs 1TB with CDN)
Origin Optimization and Cache-Control
CDN effectiveness depends entirely on cache-control headers:
import express from 'express';
const app = express();
// Origin configuration strategy
interface OriginCacheConfig {
path: string;
maxAge: number;
sMaxAge: number;
staleWhileRevalidate: number;
staleIfError: number;
}
const originConfigs: OriginCacheConfig[] = [
{
path: '/api/*',
maxAge: 0, // Browser revalidates
sMaxAge: 60, // CDN caches 1 minute
staleWhileRevalidate: 300, // Serve stale for 5 minutes while revalidating
staleIfError: 86400, // Serve stale for 24 hours if origin down
},
{
path: '/static/*',
maxAge: 31536000, // Browser caches forever
sMaxAge: 31536000,
staleWhileRevalidate: 0,
staleIfError: 0,
},
{
path: '/*.html',
maxAge: 0,
sMaxAge: 3600, // CDN caches HTML for 1 hour
staleWhileRevalidate: 86400,
staleIfError: 604800,
},
];
app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
const config = originConfigs.find(cfg =>
new RegExp(cfg.path.replace('*', '.*')).test(req.path)
);
if (config) {
const cacheControl = [
'public',
`max-age=${config.maxAge}`,
`s-maxage=${config.sMaxAge}`,
`stale-while-revalidate=${config.staleWhileRevalidate}`,
`stale-if-error=${config.staleIfError}`,
].filter(v => !v.includes('undefined')).join(', ');
res.setHeader('Cache-Control', cacheControl);
}
next();
});
// Origin health signals degradation
app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
// Under heavy load, signal CDN to serve stale
const cpuUsage = process.cpuUsage();
const memoryUsage = process.memoryUsage();
if (memoryUsage.heapUsed / memoryUsage.heapTotal > 0.9) {
// Signal to CDN that origin is struggling
res.setHeader('X-Origin-Status', 'degraded');
res.setHeader('Cache-Control', 'public, s-maxage=3600, stale-while-revalidate=604800');
}
next();
});
app.get('/api/data', (req, res) => {
res.json({ data: 'high-traffic content' });
});
Lambda@Edge for Request Manipulation
Manipulate requests/responses at CDN edges without hitting origin:
// AWS Lambda@Edge example (runs at CloudFront edge locations)
// Different syntax from normal Lambda, tied to CloudFront events
interface CloudFrontEvent {
Records: Array<{
cf: {
request: {
clientIp: string;
querystring: string;
headers: Record<string, Array<{ key: string; value: string }>>;
uri: string;
};
response?: {
status: string;
headers: Record<string, Array<{ key: string; value: string }>>;
};
};
}>;
}
// Viewer request: Before cache lookup (alter user requests)
export const viewerRequest = async (event: CloudFrontEvent) => {
const request = event.Records[0].cf.request;
// Normalize URLs (www -> non-www)
const host = request.headers.host?.[0].value || '';
if (host.startsWith('www.')) {
return {
status: '301',
statusDescription: 'Moved Permanently',
headers: {
location: [{ key: 'Location', value: `https://${host.slice(4)}${request.uri}` }],
},
};
}
// Route based on user agent
const userAgent = request.headers['cloudfront-is-mobile-viewer']?.[0].value;
if (userAgent === 'true' && !request.uri.includes('/mobile')) {
request.uri = `/mobile${request.uri}`;
}
// Add security headers
request.headers['x-forwarded-for'] = [{ key: 'X-Forwarded-For', value: request.clientIp }];
return request;
};
// Origin response: Modify response before caching at edge
export const originResponse = async (event: CloudFrontEvent) => {
const response = event.Records[0].cf.response;
const request = event.Records[0].cf.request;
// Add security headers (only at edge, reduces origin requests)
response.headers['strict-transport-security'] = [
{ key: 'Strict-Transport-Security', value: 'max-age=31536000' },
];
response.headers['x-content-type-options'] = [
{ key: 'X-Content-Type-Options', value: 'nosniff' },
];
// Vary header (important for cache accuracy)
if (response.headers.vary) {
response.headers.vary[0].value += ', Accept-Encoding';
} else {
response.headers.vary = [{ key: 'Vary', value: 'Accept-Encoding' }];
}
return response;
};
// Viewer response: Modify before sending to user
export const viewerResponse = async (event: CloudFrontEvent) => {
const response = event.Records[0].cf.response;
// Remove server-identification headers
delete response.headers['server'];
delete response.headers['x-powered-by'];
// Add performance headers
response.headers['link'] = [
{ key: 'Link', value: '</static/critical.css>; rel=preload; as=style' },
];
return response;
};
Origin Shield and Cache Behavior
Origin Shield sits between CDN edges and origin, reducing origin load:
interface CDNOriginConfig {
// Primary origin
origin: {
domain: string;
port: number;
timeout: number;
};
// Origin shield (optional tier between edge and origin)
originShield?: {
enabled: boolean;
region: string; // e.g., 'us-east-1'
};
// Secondary origin for failover
fallbackOrigin?: {
domain: string;
port: number;
};
// Cache behavior per path
cacheBehaviors: Array<{
pathPattern: string;
cachePolicy: {
defaultTtl: number;
maxTtl: number;
minTtl: number;
};
compress: boolean;
viewerProtocolPolicy: 'allow-all' | 'https-only' | 'redirect-to-https';
allowedMethods: string[];
cachedMethods: string[];
forwardedValues?: {
queryString: boolean;
cookies?: string[];
headers?: string[];
};
}>;
}
const cdnConfig: CDNOriginConfig = {
origin: {
domain: 'api.example.com',
port: 443,
timeout: 30,
},
// Origin Shield reduces origin traffic by ~80% for popular content
originShield: {
enabled: true,
region: 'us-east-1', // Should be near primary origin
},
cacheBehaviors: [
{
pathPattern: '/api/v1/public/*',
cachePolicy: { defaultTtl: 60, maxTtl: 3600, minTtl: 0 },
compress: true,
viewerProtocolPolicy: 'https-only',
allowedMethods: ['GET', 'HEAD', 'OPTIONS'],
cachedMethods: ['GET', 'HEAD'],
forwardedValues: {
queryString: true, // Include query strings in cache key
headers: ['Accept-Language', 'Accept-Encoding'],
},
},
{
pathPattern: '/api/v1/private/*',
cachePolicy: { defaultTtl: 0, maxTtl: 300, minTtl: 0 },
compress: true,
viewerProtocolPolicy: 'https-only',
allowedMethods: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'PATCH'],
cachedMethods: [],
forwardedValues: {
queryString: true,
headers: ['Authorization'],
},
},
],
};
Cache Invalidation Strategies
Invalidating stale content without purging everything:
import aws from 'aws-sdk';
const cloudfront = new aws.CloudFront();
const DISTRIBUTION_ID = process.env.CLOUDFRONT_DISTRIBUTION_ID!;
// Path-based invalidation (specific URLs)
async function invalidatePaths(paths: string[]): Promise<string> {
const result = await cloudfront.createInvalidation({
DistributionId: DISTRIBUTION_ID,
InvalidationBatch: {
CallerReference: `${Date.now()}`,
Paths: {
Quantity: paths.length,
Items: paths,
},
},
}).promise();
return result.Invalidation!.Id;
}
// Invalidate single post after update
async function invalidatePost(postId: string): Promise<void> {
const paths = [
`/blog/${postId}`,
`/blog/${postId}/*`, // All subpaths
`/api/posts/${postId}`,
`/`, // Homepage links to recent posts
`/blog`, // Blog listing page
];
const invalidationId = await invalidatePaths(paths);
console.log(`Invalidation ${invalidationId} queued for paths:`, paths);
}
// Wildcard invalidation (entire directory)
async function invalidateDirectory(path: string): Promise<void> {
await invalidatePaths([`${path}/*`]);
}
// Tag-based invalidation (if supported, e.g., Fastly)
async function invalidateByTag(tag: string): Promise<void> {
// Fastly example
const response = await fetch('https://api.fastly.com/purge', {
method: 'POST',
headers: {
'Fastly-Key': process.env.FASTLY_API_KEY!,
'Surrogate-Key': tag,
},
});
if (!response.ok) {
throw new Error(`Purge failed: ${response.statusText}`);
}
}
// Smart invalidation on content changes
async function handleContentUpdate(
contentType: 'post' | 'user' | 'config',
id: string,
changeType: 'create' | 'update' | 'delete'
): Promise<void> {
const invalidationPaths: Record<string, string[]> = {
post: [
`/blog/${id}`,
`/api/posts/${id}`,
`/`, // Homepage
`/blog`, // Blog listing
],
user: [
`/users/${id}`,
`/api/users/${id}`,
],
config: [
`/*`, // All pages use config
],
};
const paths = invalidationPaths[contentType] || [];
if (paths.length > 0) {
await invalidatePaths(paths);
}
}
Geolocation Routing and WAF
Route requests based on location, apply geographic WAF rules:
interface GeoRoutingConfig {
region: string;
originDomain: string;
wafRules: WAFRule[];
}
interface WAFRule {
name: string;
priority: number;
action: 'BLOCK' | 'ALLOW' | 'COUNT';
condition: {
type: 'ipset' | 'country' | 'regex' | 'byteMatch';
value: string[];
};
}
const geoConfig: GeoRoutingConfig[] = [
{
region: 'us',
originDomain: 'api-us.example.com',
wafRules: [
{
name: 'block-tor-exits',
priority: 1,
action: 'BLOCK',
condition: {
type: 'ipset',
value: ['192.0.2.0/24'], // Tor exit nodes
},
},
{
name: 'rate-limit-per-ip',
priority: 2,
action: 'BLOCK',
condition: {
type: 'regex',
value: ['^/api/.*'], // Block IPs making >100 requests/minute to /api/
},
},
],
},
{
region: 'eu',
originDomain: 'api-eu.example.com',
wafRules: [
{
name: 'gdpr-compliance',
priority: 1,
action: 'ALLOW',
condition: {
type: 'country',
value: ['DE', 'FR', 'GB'],
},
},
],
},
];
// Lambda function at edge to route based on geography
export async function geoRouter(event: any) {
const request = event.Records[0].cf.request;
const countryCode = request.headers['cloudfront-viewer-country']?.[0].value;
// Route to regional origin
const region = countryCode?.startsWith('US') ? 'us' : 'eu';
const config = geoConfig.find(c => c.region === region);
if (config) {
request.origin = {
custom: {
host: config.originDomain,
port: 443,
protocol: 'https',
path: '',
sslProtocols: ['TLSv1.2'],
},
};
}
return request;
}
CDN Failover and Health Checks
Automatic failover when primary origin becomes unhealthy:
interface OriginHealth {
lastCheck: Date;
isHealthy: boolean;
failureCount: number;
successCount: number;
}
class CDNFailoverManager {
private primaryOrigin: string;
private secondaryOrigin: string;
private healthStatus = new Map<string, OriginHealth>();
async checkOriginHealth(origin: string): Promise<boolean> {
try {
const response = await fetch(`https://${origin}/health`, {
timeout: 5000,
});
return response.status === 200;
} catch (error) {
console.error(`Health check failed for ${origin}:`, error);
return false;
}
}
async monitorOrigins(): Promise<void> {
const primaryHealthy = await this.checkOriginHealth(this.primaryOrigin);
const secondaryHealthy = await this.checkOriginHealth(this.secondaryOrigin);
this.updateHealthStatus(this.primaryOrigin, primaryHealthy);
this.updateHealthStatus(this.secondaryOrigin, secondaryHealthy);
// Update CDN configuration if primary became unhealthy
if (!primaryHealthy && this.healthStatus.get(this.primaryOrigin)!.failureCount > 3) {
await this.switchToSecondaryOrigin();
}
}
private updateHealthStatus(origin: string, isHealthy: boolean): void {
const status = this.healthStatus.get(origin) || {
lastCheck: new Date(),
isHealthy: true,
failureCount: 0,
successCount: 0,
};
if (isHealthy) {
status.successCount++;
status.failureCount = 0;
} else {
status.failureCount++;
if (status.failureCount > 1) {
status.successCount = 0;
}
}
status.isHealthy = status.failureCount === 0;
status.lastCheck = new Date();
this.healthStatus.set(origin, status);
}
private async switchToSecondaryOrigin(): Promise<void> {
console.warn('Switching to secondary origin due to primary failure');
// Update CDN distribution to use secondary origin
// AWS CloudFront example:
// cloudfront.updateDistribution({
// DistributionConfig: {
// Origins: [
// { DomainName: this.secondaryOrigin, ... }
// ]
// }
// })
// Notify operations team
await sendAlert({
level: 'critical',
message: 'CDN failover activated',
details: {
primaryOrigin: this.primaryOrigin,
secondaryOrigin: this.secondaryOrigin,
},
});
}
}
Measuring CDN Hit Ratio
Monitor cache effectiveness:
interface CDNMetrics {
timestamp: Date;
requestsFromEdge: number;
requestsFromOrigin: number;
hitRatio: number;
bandwidthSaved: number; // bytes
originLoad: number; // percentage
}
class CDNMetricsCollector {
async collectMetrics(): Promise<CDNMetrics> {
// Get CloudFront statistics
const cloudfront = new aws.CloudFront();
const stats = await cloudfront.getDistributionStatistics({
DistributionId: process.env.CLOUDFRONT_DISTRIBUTION_ID!,
}).promise();
const edgeRequests = stats.EdgeRequests || 0;
const originRequests = stats.OriginRequests || 0;
const hitRatio = edgeRequests / (edgeRequests + originRequests);
return {
timestamp: new Date(),
requestsFromEdge: edgeRequests,
requestsFromOrigin: originRequests,
hitRatio,
bandwidthSaved: (edgeRequests * 1024 * 1024), // Estimate 1MB average
originLoad: (originRequests / (edgeRequests + originRequests)) * 100,
};
}
async analyzeMetrics(metrics: CDNMetrics): Promise<string[]> {
const recommendations: string[] = [];
if (metrics.hitRatio < 0.8) {
recommendations.push('CDN hit ratio below 80%, review cache-control headers');
}
if (metrics.originLoad > 0.3) {
recommendations.push('Origin handling >30% of requests, consider origin shield');
}
return recommendations;
}
}
Checklist
- Set appropriate Cache-Control headers (max-age, s-maxage, stale-*)
- Configure CDN origin shield to reduce origin load
- Implement smart cache invalidation (path-specific, not wildcard)
- Use Lambda@Edge for request rewriting without origin load
- Monitor CDN hit ratio and tune cache policies
- Implement health checks and automatic failover
- Add WAF rules at CDN layer (geo-blocking, rate limiting)
- Use geolocation routing for regional origin optimization
- Test failover procedures quarterly
- Monitor cost vs performance (CDN bandwidth vs origin egress)
Conclusion
CDNs multiply origin capacity by caching at the edge. But without proper configuration—cache headers, origin shield, intelligent invalidation—they become expensive pass-throughs. Understanding the layered architecture, from viewer requests to origin responses, transforms a CDN from a cost center into a force multiplier for global scale.