DEV Community

ahmet gedik
ahmet gedik

Posted on

Cloudflare Workers for European Content Edge Caching

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));
});
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

  1. Use Workers for programmable cache logic, not just Page Rules
  2. Vary cache keys by region for multi-region content
  3. Implement stale-while-revalidate for always-fast responses
  4. Purge selectively after content updates
  5. Leverage Cloudflare's European edge network for European audiences

Part of the "Building ViralVidVault" series.

Top comments (0)