The cloud was just the beginning. Edge computing moves computation closer to users, reducing latency, improving performance, and enabling new use cases that aren’t possible with centralized infrastructure. This guide covers edge architecture patterns, CDN optimization, and serverless at the edge.
Understanding Edge Computing
Traditional cloud architecture sends all requests to centralized data centers. Edge computing distributes computation across a global network of edge locations:
- CDN Points of Presence (PoPs): 250+ locations worldwide
- Edge compute: Run code at edge locations
- Edge databases: Data stored close to users
- IoT edge: Compute on devices
Benefits include reduced latency (typically 10-50ms vs 100-300ms to origin), better user experience, reduced bandwidth costs, and improved resilience.
CDN Deep Dive
CDN Architecture
graph TD
User1[User US-East] --> CDN1[Edge PoP Virginia]
User2[User Europe] --> CDN2[Edge PoP Frankfurt]
User3[User Asia] --> CDN3[Edge PoP Tokyo]
CDN1 --> Origin[Origin Server]
CDN2 --> Origin
CDN3 --> Origin
CDN1 --> Cache1[Cache: Static Assets]
CDN2 --> Cache2[Cache: Static Assets]
CDN3 --> Cache3[Cache: Static Assets]
CDN Configuration Examples
CloudFront with Origin Shield:
# CloudFront distribution
AWSTemplateFormatVersion: '2010-09-09'
Resources:
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Enabled: true
DefaultRootObject: index.html
OriginShieldRegion: us-east-1
Origins:
- Id: MyOrigin
DomainName: myapp.example.com
CustomOriginConfig:
OriginProtocolPolicy: https-only
HTTPPort: 80
HTTPSPort: 443
DefaultCacheBehavior:
TargetOriginId: MyOrigin
ViewerProtocolPolicy: redirect-to-https
Compress: true
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # Managed-CachingOptimized
CacheBehaviors:
- PathPattern: /api/*
TargetOriginId: MyOrigin
ViewerProtocolPolicy: https-only
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # Managed-CachingDisabled
- PathPattern: /static/*
TargetOriginId: MyOrigin
ViewerProtocolPolicy: redirect-to-https
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
OriginRequestPolicyId: 88a5e3d4-0532-4a71-aca8-bb49f4f1b8e4 # Managed-ViewerAccess
Cloudflare Page Rules:
# Cloudflare Workers + Page Rules
name: Edge Cache Config
# Cache everything at edge
routes:
- pattern: "*.example.com/static/*"
zone_name: "example.com"
workers:
- name: cache-api
script: |
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const cache = caches.default
const response = await cache.match(request)
if (response) {
return response
}
const fetched = await fetch(request)
const newResponse = fetched.clone()
event.waitUntil(cache.put(request, newResponse))
return fetched
}
Cache Invalidation Strategies
# CloudFront invalidation script
import boto3
def invalidate_cloudfront(distribution_id, paths):
"""Invalidate CloudFront cache for specific paths"""
client = boto3.client('cloudfront')
response = client.create_invalidation(
DistributionId=distribution_id,
InvalidationBatch={
'CallerReference': f'invalidation-{datetime.now().timestamp()}',
'Paths': {
'Quantity': len(paths),
'Items': paths
}
}
)
return response['Invalidation']['Id']
# Usage
invalidate_cloudfront(
'E1234567890ABC',
[
'/static/css/*.css',
'/static/js/*.js',
'/api/config'
]
)
Serverless at the Edge
Edge Function Platforms
Modern edge platforms let you run code closer to users:
| Platform | Edge Locations | Language Support | Cold Start |
|---|---|---|---|
| Cloudflare Workers | 300+ | JavaScript, Rust, Python, C++ | <5ms |
| AWS Lambda@Edge | 200+ | Node.js, Python, Go, Java | ~100ms |
| Vercel Edge | 35+ | JavaScript, TypeScript | <10ms |
| Deno Deploy | 40+ | JavaScript, TypeScript | <10ms |
Cloudflare Workers Example
// Cloudflare Worker - Authentication at Edge
export default {
async fetch(request, env) {
// Parse JWT at edge
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (!token) {
return new Response('Unauthorized', { status: 401 });
}
try {
// Verify JWT without round-trip to origin
const payload = await verifyJWT(token, env.JWT_SECRET);
// Add user context to headers
const headers = new Headers(request.headers);
headers.set('X-User-ID', payload.userId);
headers.set('X-User-Role', payload.role);
// Forward to origin with user context
return fetch(request, { headers });
} catch (e) {
return new Response('Invalid token', { status: 403 });
}
}
}
async function verifyJWT(token, secret) {
const parts = token.split('.');
const header = JSON.parse(atob(parts[0]));
const payload = JSON.parse(atob(parts[1]));
const signature = parts[2];
// In production, use a proper library
// This is simplified for illustration
return payload;
}
Lambda@Edge Examples
// Lambda@Edge - Request origin
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
// Add security headers
request.headers['x-edge-function'] = [{ key: 'x-edge-function', value: 'processed' }];
// A/B testing at edge
const cookies = parseCookies(request.headers['cookie']);
if (!cookies.variant) {
const variant = Math.random() > 0.5 ? 'a' : 'b';
const response = {
status: '302',
headers: {
'location': [{ key: 'location', value: request.uri }],
'set-cookie': [
{ key: 'set-cookie', value: `variant=${variant}; Path=/; Max-Age=2592000` }
]
}
};
callback(null, response);
return;
}
// Forward request with variant header
request.headers['x-variant'] = [{ key: 'x-variant', value: cookies.variant }];
callback(null, request);
};
function parseCookies(cookieHeader) {
if (!cookieHeader) return {};
return cookieHeader.split(';').reduce((cookies, cookie) => {
const [key, value] = cookie.trim().split('=');
cookies[key] = value;
return cookies;
}, {});
}
Vercel Edge Functions
// Vercel Edge Function
import { NextRequest, NextResponse } from 'next/server';
export const config = {
runtime: 'edge',
regions: ['iad1', 'sfo1', 'fra1'], // Specify regions
};
export default async function handler(req: NextRequest) {
const geo = req.geo;
// Personalize based on user location
const content = {
city: geo?.city || 'Unknown',
country: geo?.country || 'Unknown',
region: geo?.region || 'Unknown',
lat: geo?.latitude,
lon: geo?.longitude,
};
// Fetch user-specific data from edge database
const userData = await getUserData(req.headers.get('x-user-id'));
return NextResponse.json({
...content,
user: userData,
});
}
async function getUserData(userId: string | null) {
if (!userId) return null;
// Use edge-compatible database (e.g., Turso, D1)
const result = await fetch(
`https://db.example.com/user/${userId}`,
{ cache: 'no-store' }
);
return result.json();
}
Edge Databases
Edge Database Options
| Database | Type | Edge Replicas | Latency | Use Case |
|---|---|---|---|---|
| Cloudflare D1 | SQLite | 100+ | <10ms | Serverless apps |
| Turso | SQLite | 40+ | <20ms | Global apps |
| PlanetScale | MySQL | 50+ | <50ms | Full SQL |
| Fauna | Document | 10+ | <100ms | Flexible schema |
| Redis Enterprise | KV | 100+ | <10ms | Caching |
Cloudflare D1 Example
-- D1 Database Schema
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
name TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER REFERENCES users(id),
title TEXT NOT NULL,
content TEXT,
published_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_posts_user ON posts(user_id);
CREATE INDEX idx_posts_published ON posts(published_at DESC);
// Cloudflare Worker with D1
export default {
async fetch(request, env) {
const url = new URL(request.url);
const path = url.pathname;
if (path === '/api/users' && request.method === 'GET') {
const { results } = await env.DB.prepare(
'SELECT * FROM users ORDER BY created_at DESC LIMIT 10'
).all();
return Response.json(results);
}
if (path === '/api/users' && request.method === 'POST') {
const { email, name } = await request.json();
const { success } = await env.DB.prepare(
'INSERT INTO users (email, name) VALUES (?, ?)'
).bind(email, name).run();
return Response.json({ success });
}
return new Response('Not Found', { status: 404 });
}
};
Global Architecture Patterns
Multi-Region Active-Active
# Terraform - Multi-region deployment
terraform {
required_providers {
aws = { source = "hashicorp/aws" }
}
}
# Primary region
provider "aws" {
alias = "primary"
region = "us-east-1"
}
# Secondary region
provider "aws" {
alias = "secondary"
region = "us-west-2"
}
# Global DynamoDB
resource "aws_dynamodb_table" "global" {
name = "edge-app-global"
billing_mode = "PAY_PER_REQUEST"
hash_key = "pk"
range_key = "sk"
attribute {
name = "pk"
type = "S"
}
attribute {
name = "sk"
type = "S"
}
replica {
region_name = "us-east-1"
}
replica {
region_name = "us-west-2"
}
server_side_encryption {
enabled = true
}
}
# CloudFront with multi-origin
resource "aws_cloudfront_distribution" "global" {
enabled = true
price_class = "PriceAll"
comment = "Global edge deployment"
origin {
domain_name = "primary.example.com"
origin_id = "primary"
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
ssl_protocols = ["TLSv1.2"]
}
}
origin {
domain_name = "secondary.example.com"
origin_id = "secondary"
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
ssl_protocols = ["TLSv1.2"]
}
}
default_cache_behavior {
target_origin_id = "primary"
viewer_protocol_policy = "redirect-to-https"
# Use origin groups for failover
origin_group_id = aws_cloudfront_origin_group.primary.id
}
origin_group {
id = "primary"
members {
origin_id = "primary"
}
failover_criteria {
status_codes = [403, 404, 500, 502, 503, 504]
}
}
}
Edge-Based A/B Testing
// Edge A/B Testing with Cookie
export default {
async fetch(request, env) {
const url = new URL(request.url);
// Check for existing variant
let variant = getCookie(request, 'ab-variant');
if (!variant) {
// Assign new variant
variant = Math.random() < 0.5 ? 'control' : 'treatment';
}
// Fetch experiment config from KV
const experiment = await env.EXPERIMENTS.get(`experiment:${variant}`, 'json');
// Add experiment headers for origin
const response = await fetch(request);
// Set cookie if not present
if (!getCookie(request, 'ab-variant')) {
const headers = new Headers(response.headers);
headers.set('Set-Cookie', `ab-variant=${variant}; Path=/; Max-Age=2592000; SameSite=Lax`);
return new Response(response.body, {
status: response.status,
headers
});
}
return response;
}
};
function getCookie(request, name) {
const cookie = request.headers.get('Cookie');
if (!cookie) return null;
const match = cookie.match(new RegExp(`(${name})=([^;]+)`));
return match ? match[2] : null;
}
Performance Optimization
Edge-Side Image Optimization
// Cloudflare Worker - Image resizing
export default {
async fetch(request, env) {
const url = new URL(request.url);
const imageUrl = url.searchParams.get('url');
const width = url.searchParams.get('w') || '800';
const format = url.searchParams.get('f') || 'webp';
// Use Cloudflare Images
const variants = await fetch(
`https://api.cloudflare.com/client/v4/accounts/${env.CF_ACCOUNT_ID}/images/v1/variants/${format}`,
{
headers: {
'Authorization': `Bearer ${env.CF_API_TOKEN}`
}
}
).then(r => r.json());
// Redirect to resized image
const imageId = imageUrl.split('/').pop();
return Response.redirect(
`https://imagedelivery.net/${env.CF_ACCOUNT_ID}/${imageId}/${format}?width=${width}`,
302
);
}
};
Latency Optimization
# Predictive prefetching with Service Worker
// sw.js
const CACHE_NAME = 'edge-cache-v1';
const PREFETCH_URLS = ['/api/popular', '/api/config'];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(PREFETCH_URLS))
);
});
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
// Prefetch likely navigation
if (event.request.mode === 'navigate') {
event.respondWith(
fetch(event.request)
.then(response => {
const clone = response.clone();
caches.open(CACHE_NAME)
.then(cache => cache.put(event.request, clone));
return response;
})
.catch(() => caches.match(event.request))
);
}
// Stale-while-revalidate for API
if (url.pathname.startsWith('/api/')) {
event.respondWith(
caches.open(CACHE_NAME)
.then(cache => {
const cached = cache.match(event.request);
return fetch(event.request)
.then(response => {
cache.put(event.request, response.clone());
return response;
})
.catch(() => cached);
})
);
}
});
Monitoring Edge Performance
# Edge monitoring with Prometheus
groups:
- name: edge
rules:
- alert: HighEdgeLatency
expr: histogram_quantile(0.95, rate(cloudflare_edge_latency_bucket[5m])) > 0.5
for: 5m
labels:
severity: warning
annotations:
summary: "High edge latency detected"
- alert: CacheHitRateLow
expr: (sum(rate(cloudflare_cache_hits[5m])) / sum(rate(cloudflare_cache_requests[5m]))) < 0.7
for: 10m
labels:
severity: warning
annotations:
summary: "Cache hit rate below 70%"
- alert: EdgeErrors
expr: sum(rate(cloudflare_errors[5m])) > 10
for: 2m
labels:
severity: critical
annotations:
summary: "Edge errors detected"
Cost Optimization
Edge computing can reduce costs significantly:
| Optimization | Potential Savings |
|---|---|
| Cache static assets | 60-80% bandwidth |
| Edge compute vs origin | 40-60% compute |
| Reduced data transfer | 30-50% egress |
| Serverless edge | 70-90% vs VMs |
# Cost monitoring
resource "aws_cloudwatch_metric_alert" "cdncost" {
alarm_name = "cdn-cost-threshold"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
metric_name = "EstimatedCharges"
namespace = "AWS/Cost and Usage"
period = 86400
statistic = "Maximum"
threshold = 1000
treat_missing_data = "notBreaching"
dimensions = {
Service = "AmazonCloudFront"
}
}
Conclusion
Edge computing transforms how we build global applications:
- Use CDNs for all static content with proper cache headers
- Deploy edge functions for personalization and authentication
- Choose edge databases for globally distributed data
- Implement multi-region active-active for critical applications
- Monitor edge performance separately from origin
Start with CDN optimization, add edge functions for common use cases, then consider edge databases for global applications.
External Resources
Related Articles
- Data Transfer Costs - Edge cost optimization
- Multi-Cloud Strategy - Global architecture
- Serverless Cost Traps - Edge vs serverless costs
Comments