Beyond CDN Caching: Programmable Edge Caching
Traditional CDN caching is limited to static assets and simple cache-control rules. Edge compute platforms let you write actual caching logic — personalized responses, conditional caching, and smart invalidation — running in hundreds of locations worldwide.
A Basic Edge Cache-Aside Pattern
export default {
async fetch(request, env) {
const cacheKey = new Request(request.url, request);
const cache = caches.default;
let response = await cache.match(cacheKey);
if (response) return response;
response = await fetch(request);
response = new Response(response.body, response);
response.headers.set('Cache-Control', 'max-age=300');
ctx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
},
};
Using KV for Structured Edge Data
export default {
async fetch(request, env) {
const url = new URL(request.url);
const productId = url.pathname.split('/').pop();
const cached = await env.PRODUCT_CACHE.get(productId, { type: 'json' });
if (cached) return Response.json(cached);
const product = await fetchProductFromOrigin(productId);
await env.PRODUCT_CACHE.put(productId, JSON.stringify(product), {
expirationTtl: 3600,
});
return Response.json(product);
},
};
Smart Invalidation on Content Updates
// Origin webhook triggered when a product is updated
async function handleProductUpdate(request, env) {
const { productId } = await request.json();
await env.PRODUCT_CACHE.delete(productId);
// Also purge the CDN-level cache for the rendered page
await fetch('https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache', {
method: 'POST',
headers: { Authorization: `Bearer ${env.CF_API_TOKEN}` },
body: JSON.stringify({ files: [`https://example.com/products/${productId}`] }),
});
}
Stale-While-Revalidate at the Edge
async function staleWhileRevalidate(cacheKey, cache, ctx, fetchFresh) {
const cached = await cache.match(cacheKey);
if (cached) {
ctx.waitUntil(
fetchFresh().then(fresh => cache.put(cacheKey, fresh.clone()))
);
return cached;
}
const fresh = await fetchFresh();
ctx.waitUntil(cache.put(cacheKey, fresh.clone()));
return fresh;
}
This serves a slightly stale response immediately while refreshing the cache in the background — users never wait for a cache miss on already-cached content.
Handling Personalized Content at the Edge
Vary your cache key by the dimensions that actually affect the response (locale, currency, auth status) rather than caching per-user, which defeats the purpose of edge caching:
const cacheKey = new Request(
`${request.url}?locale=${locale}¤cy=${currency}`,
request
);
Conclusion
Edge caching earns its complexity when you need caching logic beyond what static CDN rules can express — personalization-aware keys, webhook-driven invalidation, and stale-while-revalidate patterns that keep responses both fast and reasonably fresh.