What is a CDN?
CDN (Content Delivery Network) is a distributed network of servers strategically positioned around the world to deliver web content to users from the nearest location. CDNs cache static content like images, videos, CSS, and JavaScript files, significantly reducing load times and improving user experience.
How CDN Works
Basic Architecture
User in New York → CDN Edge Server (New York)
User in London → CDN Edge Server (London)
User in Tokyo → CDN Edge Server (Tokyo)
↓
All edge servers sync with Origin Server
Benefits:
- Reduced latency (shorter distance)
- Lower bandwidth costs
- Improved availability
- DDoS protection
Request Flow
// Without CDN
User → Origin Server (3000ms)
// With CDN
User → Nearest Edge Server (50ms) → [Cache Hit]
User → Nearest Edge Server (50ms) → Origin Server (300ms) → [Cache Miss]
// Typical CDN URL structure
const cdnUrl = 'https://cdn.example.com/images/logo.png';
const originUrl = 'https://www.example.com/images/logo.png';
CDN Integration
Using CDN for Static Assets
// Next.js with CDN
export default {
assetPrefix: process.env.NODE_ENV === 'production'
? 'https://cdn.example.com'
: '',
images: {
domains: ['cdn.example.com'],
loader: 'custom',
loaderFile: './cdn-loader.ts'
}
};
// Custom loader
export default function cdnLoader({ src, width, quality }: any) {
const params = new URLSearchParams({
url: src,
w: width.toString(),
q: (quality || 75).toString()
});
return `https://cdn.example.com/img?${params}`;
}
Cloudflare CDN Setup
// Configure cache headers for Cloudflare CDN
export async function GET(request: Request) {
const response = new Response('Hello from CDN', {
headers: {
'Content-Type': 'text/html',
// Cache for 1 hour
'Cache-Control': 'public, max-age=3600',
// Cloudflare specific
'CDN-Cache-Control': 'max-age=7200',
// Edge cache tag for purging
'Cache-Tag': 'homepage, v2'
}
});
return response;
}
Programmatic CDN Purge
class CloudflareCDN {
private apiToken: string;
private zoneId: string;
constructor(apiToken: string, zoneId: string) {
this.apiToken = apiToken;
this.zoneId = zoneId;
}
async purgeCache(urls: string[]) {
const response = await fetch(
`https://api.cloudflare.com/client/v4/zones/${this.zoneId}/purge_cache`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
files: urls
})
}
);
return response.json();
}
async purgeEverything() {
const response = await fetch(
`https://api.cloudflare.com/client/v4/zones/${this.zoneId}/purge_cache`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
purge_everything: true
})
}
);
return response.json();
}
async purgeByTags(tags: string[]) {
const response = await fetch(
`https://api.cloudflare.com/client/v4/zones/${this.zoneId}/purge_cache`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
tags
})
}
);
return response.json();
}
}
// Usage
const cdn = new CloudflareCDN(
process.env.CF_API_TOKEN!,
process.env.CF_ZONE_ID!
);
// Purge specific URLs
await cdn.purgeCache([
'https://example.com/style.css',
'https://example.com/script.js'
]);
// Purge by cache tags
await cdn.purgeByTags(['homepage', 'products']);
CDN Caching Strategies
Cache-Control Headers
// Cache static assets aggressively
function staticAssetHeaders() {
return {
// Browser cache: 7 days
// CDN cache: 30 days
'Cache-Control': 'public, max-age=604800, s-maxage=2592000, immutable'
};
}
// Cache API responses with revalidation
function apiHeaders() {
return {
// Browser: 5 minutes, must revalidate
// CDN: 10 minutes
'Cache-Control': 'public, max-age=300, s-maxage=600, must-revalidate'
};
}
// Don't cache dynamic content
function dynamicHeaders() {
return {
'Cache-Control': 'private, no-cache, no-store, must-revalidate',
'Expires': '0'
};
}
Stale-While-Revalidate
// Serve stale content while fetching fresh data
export async function GET() {
return new Response(JSON.stringify({ data: 'value' }), {
headers: {
'Content-Type': 'application/json',
// Serve from cache for 1 hour
// Serve stale for 24 hours while revalidating
'Cache-Control': 'max-age=3600, stale-while-revalidate=86400'
}
});
}
CDN with Web Scraping
Bypassing CDN Protection
async function scrapeWithCDNBypass(url: string) {
// Some CDNs block automated requests
// Use CorsProxy to handle CDN challenges
const response = await fetch(
`https://corsproxy.io/?url=${url}`,
{
headers: {
'x-cors-api-key': process.env.CORS_API_KEY!,
'x-cors-proxy-type': 'residential',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0'
}
}
);
return response.text();
}
Respecting CDN Caching
async function respectCDNCache(url: string) {
const response = await fetch(url);
// Check CDN headers
const cacheStatus = response.headers.get('cf-cache-status'); // Cloudflare
const cdnHit = response.headers.get('x-cache'); // General
console.log('Cache Status:', cacheStatus); // HIT, MISS, EXPIRED, etc.
console.log('CDN Hit:', cdnHit);
// Only scrape if needed (cache miss)
if (cacheStatus === 'MISS') {
console.log('Fresh fetch from origin');
}
return response.text();
}
Image CDN Optimization
Dynamic Image Transformation
interface ImageTransformOptions {
width?: number;
height?: number;
quality?: number;
format?: 'webp' | 'avif' | 'jpeg' | 'png';
fit?: 'cover' | 'contain' | 'fill';
}
class ImageCDN {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
getImageUrl(path: string, options: ImageTransformOptions = {}): string {
const params = new URLSearchParams();
if (options.width) params.append('w', options.width.toString());
if (options.height) params.append('h', options.height.toString());
if (options.quality) params.append('q', options.quality.toString());
if (options.format) params.append('f', options.format);
if (options.fit) params.append('fit', options.fit);
return `${this.baseUrl}/${path}?${params}`;
}
}
// Usage
const cdn = new ImageCDN('https://cdn.example.com');
// Get optimized image URLs
const thumbnail = cdn.getImageUrl('products/shoe.jpg', {
width: 300,
height: 300,
quality: 80,
format: 'webp',
fit: 'cover'
});
const fullsize = cdn.getImageUrl('products/shoe.jpg', {
width: 1920,
quality: 90,
format: 'webp'
});
CDN Edge Functions
Cloudflare Workers
// Deploy logic at CDN edge
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
// A/B testing at the edge
const variant = Math.random() > 0.5 ? 'A' : 'B';
// Rewrite URL based on variant
url.pathname = `/variants/${variant}${url.pathname}`;
// Fetch from origin
const response = await fetch(url.toString());
// Add custom headers
const newResponse = new Response(response.body, response);
newResponse.headers.set('X-Variant', variant);
return newResponse;
}
};
Geographic Routing
export default {
async fetch(request: Request): Promise<Response> {
// Get user location from CDN
const country = request.headers.get('cf-ipcountry');
const continent = request.headers.get('cf-ipcontinent');
// Route to regional API
const apiEndpoint = getRegionalEndpoint(country || 'US');
const response = await fetch(apiEndpoint, {
method: request.method,
headers: request.headers,
body: request.body
});
return response;
}
};
function getRegionalEndpoint(country: string): string {
const regions: Record<string, string> = {
'US': 'https://us-api.example.com',
'EU': 'https://eu-api.example.com',
'APAC': 'https://apac-api.example.com'
};
return regions[country] || regions['US'];
}
CDN Performance Monitoring
Measuring CDN Performance
async function measureCDNPerformance(url: string) {
const startTime = performance.now();
const response = await fetch(url);
const endTime = performance.now();
const loadTime = endTime - startTime;
const metrics = {
loadTime: `${loadTime.toFixed(2)}ms`,
cacheStatus: response.headers.get('cf-cache-status'),
cdnRay: response.headers.get('cf-ray'),
dataCenter: response.headers.get('cf-ray')?.split('-')[1],
contentLength: response.headers.get('content-length'),
contentType: response.headers.get('content-type')
};
return metrics;
}
// Compare CDN vs Origin
async function compareCDNvsOrigin() {
const cdnUrl = 'https://cdn.example.com/image.jpg';
const originUrl = 'https://origin.example.com/image.jpg';
const [cdnMetrics, originMetrics] = await Promise.all([
measureCDNPerformance(cdnUrl),
measureCDNPerformance(originUrl)
]);
console.log('CDN:', cdnMetrics);
console.log('Origin:', originMetrics);
}
Security Features
DDoS Protection
// CDN provides automatic DDoS protection
// Configure rate limiting at edge
interface RateLimitConfig {
maxRequests: number;
windowSeconds: number;
action: 'block' | 'challenge' | 'log';
}
const rateLimitConfig: RateLimitConfig = {
maxRequests: 100,
windowSeconds: 60,
action: 'challenge'
};
// Cloudflare Worker implementation
export default {
async fetch(request: Request, env: any): Promise<Response> {
const ip = request.headers.get('cf-connecting-ip') || '';
// Check rate limit
const rateLimitKey = `ratelimit:${ip}`;
const count = await env.KV.get(rateLimitKey);
if (count && parseInt(count) > rateLimitConfig.maxRequests) {
return new Response('Rate limit exceeded', { status: 429 });
}
// Increment counter
await env.KV.put(rateLimitKey, (parseInt(count || '0') + 1).toString(), {
expirationTtl: rateLimitConfig.windowSeconds
});
return fetch(request);
}
};
Best Practices
For Website Owners
- Use long cache times for static assets
- Implement cache-busting strategies
- Configure proper cache headers
- Use CDN purging when content updates
- Enable compression (gzip, brotli)
For Developers
- Use CDN URLs for all static assets
- Implement fallback to origin
- Monitor CDN performance metrics
- Test cache behavior thoroughly
- Use edge functions for dynamic logic
Performance Optimization
- Minimize DNS lookups
- Use HTTP/2 or HTTP/3
- Enable early hints
- Implement image optimization
- Use prefetch/preconnect headers