Introduction
When your users are spread across Europe — from Warsaw to Amsterdam to Stockholm — edge caching becomes essential. At ViralVidVault, we use Cloudflare's network to cache content close to European users. Here's our edge caching strategy.
The Challenge
Our origin server is in a single location, but our users are across 7 regions: US, GB, Poland, Netherlands, Sweden, Norway, and Austria. Without edge caching, a user in Oslo gets served from the origin every time, adding 100-200ms of latency.
Cloudflare Page Rules vs Workers
Page Rules are simpler but limited. Workers give you programmable edge logic:
// cloudflare-worker.js
// Smart edge caching for video platform
const CACHE_TTLS = {
'/': 10800, // Home: 3 hours
'/category/': 10800, // Categories: 3 hours
'/watch/': 21600, // Watch pages: 6 hours
'/search': 600, // Search: 10 minutes
};
async function handleRequest(request) {
const url = new URL(request.url);
const cache = caches.default;
// Only cache GET requests
if (request.method !== 'GET') {
return fetch(request);
}
// Check edge cache
const cacheKey = new Request(url.toString(), request);
let response = await cache.match(cacheKey);
if (response) {
// Cache hit
const newHeaders = new Headers(response.headers);
newHeaders.set('X-Edge-Cache', 'HIT');
return new Response(response.body, {
status: response.status,
headers: newHeaders,
});
}
// Cache miss - fetch from origin
response = await fetch(request);
if (response.ok) {
const ttl = getCacheTTL(url.pathname);
if (ttl > 0) {
const cachedResponse = new Response(response.body, response);
cachedResponse.headers.set('Cache-Control', `public, max-age=${ttl}`);
cachedResponse.headers.set('X-Edge-Cache', 'MISS');
// Store in edge cache (non-blocking)
const event = new FetchEvent('fetch', { request });
event.waitUntil(cache.put(cacheKey, cachedResponse.clone()));
return cachedResponse;
}
}
return response;
}
function getCacheTTL(pathname) {
for (const [prefix, ttl] of Object.entries(CACHE_TTLS)) {
if (pathname.startsWith(prefix) || pathname === prefix) {
return ttl;
}
}
return 3600; // Default 1 hour
}
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
Region-Aware Cache Keys
For a multi-region platform, the same URL might serve different content based on the user's country. Cloudflare provides cf.country to handle this:
function getRegionCacheKey(request) {
const url = new URL(request.url);
const country = request.cf?.country || 'US';
// Only vary by region for pages that show regional content
const regionalPaths = ['/trending', '/category/'];
const isRegional = regionalPaths.some(p => url.pathname.startsWith(p));
if (isRegional) {
// Map to our supported regions
const regionMap = {
'PL': 'PL', 'NL': 'NL', 'SE': 'SE',
'NO': 'NO', 'AT': 'AT', 'GB': 'GB',
};
const region = regionMap[country] || 'US';
url.searchParams.set('_region', region);
}
return new Request(url.toString(), request);
}
Cache Purging After Content Updates
When the cron job fetches new videos, we need to purge the edge cache:
<?php
// After cron fetch completes
class CloudflarePurger
{
private string $zoneId;
private string $apiToken;
public function __construct(string $zoneId, string $apiToken)
{
$this->zoneId = $zoneId;
$this->apiToken = $apiToken;
}
public function purgeUrls(array $urls): bool
{
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "https://api.cloudflare.com/client/v4/zones/{$this->zoneId}/purge_cache",
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer {$this->apiToken}",
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['files' => $urls]),
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
return $data['success'] ?? false;
}
public function purgeEverything(): bool
{
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "https://api.cloudflare.com/client/v4/zones/{$this->zoneId}/purge_cache",
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer {$this->apiToken}",
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['purge_everything' => true]),
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
return $data['success'] ?? false;
}
}
Stale-While-Revalidate
The stale-while-revalidate directive is a game-changer for video platforms. It lets the edge serve expired content while fetching a fresh copy in the background:
Cache-Control: public, max-age=10800, stale-while-revalidate=7200
This means: cache for 3 hours, then for the next 2 hours serve stale while revalidating. Users always get an instant response.
At ViralVidVault, we use this extensively:
| Page | max-age | stale-while-revalidate |
|---|---|---|
| Home/Category | 3h | 2h |
| Watch | 6h | 24h |
| Search | 10min | 30min |
European Edge Locations
Cloudflare has data centers in Warsaw, Amsterdam, Stockholm, Oslo, and Vienna — exactly where our users are. This means cached content for viralvidvault.com is served from a server just miles from the user, not from across an ocean.
Key Takeaways
- Use Workers for programmable cache logic, not just Page Rules
- Vary cache keys by region for multi-region content
- Implement stale-while-revalidate for always-fast responses
- Purge selectively after content updates
- Leverage Cloudflare's European edge network for European audiences
Part of the "Building ViralVidVault" series.
Top comments (0)