Cloudflare Workers ile Edge Fonksiyon Yazımı

Cloudflare’in global ağına kod yazıp deploy etmek, birkaç yıl önce hayal bile edemeyeceğimiz bir esneklik sunuyor bize. Sunucu kurmadan, ölçeklendirme derdine girmeden, kullanıcıya en yakın noktadan kod çalıştırmak istiyorsanız Cloudflare Workers tam size göre. Bu yazıda Workers’ı sysadmin gözüyle ele alacağız; teoride boğulmadan, gerçek dünyada ne işe yaradığını göstereceğiz.

Cloudflare Workers Nedir, Neden Önemli?

Workers, Cloudflare’in 300’den fazla lokasyona yayılmış edge ağında JavaScript, TypeScript, Rust veya Python kodu çalıştırmanıza izin veren bir platform. Geleneksel sunucu mimarisinde bir istek gelir, veri merkezine gider, işlenir, geri döner. Workers’ta ise istek kullanıcıya en yakın Cloudflare PoP’unda (Point of Presence) karşılanır.

Bir sysadmin olarak şunu sormak isteyebilirsiniz: “Bunu zaten Nginx veya HAProxy ile yapamaz mıyım?” Yapabilirsiniz, ama Workers şunları da beraberinde getirir:

  • Sıfır sunucu yönetimi: Patch, reboot, disk dolu gibi dertler yok
  • Otomatik ölçeklendirme: Trafik patlamasında ek konfigürasyon gerekmez
  • Milisaniye altı cold start: V8 izolat mimarisi sayesinde geleneksel container başlatmadan çok daha hızlı
  • Ücretsiz katman: Günde 100.000 istek ücretsiz, küçük projeler için yeterli
  • DNS entegrasyonu: Cloudflare DNS yönetimiyle doğrudan entegre çalışır

Geliştirme Ortamını Kurmak

Workers geliştirme için Wrangler CLI aracını kullanacağız. Node.js kurulu olduğunu varsayıyorum.

# Wrangler'ı global olarak kur
npm install -g wrangler

# Versiyonu kontrol et
wrangler --version

# Cloudflare hesabınla bağlan
wrangler login

Login işlemi tarayıcı açar, Cloudflare hesabınıza giriş yaparsınız ve token otomatik kaydedilir. CI/CD ortamlarında ise token’ı elle vermek gerekir:

# CI ortamı için API token ile authenticate
export CLOUDFLARE_API_TOKEN="your_api_token_here"
wrangler whoami

API token’ı Cloudflare Dashboard’dan oluşturun: My Profile > API Tokens > Create Token. “Edit Cloudflare Workers” template’ini kullanırsanız gerekli tüm izinler otomatik gelir.

İlk projeyi oluşturalım:

# Yeni Worker projesi oluştur
wrangler init my-edge-worker
cd my-edge-worker

# Geliştirme sunucusunu başlat
wrangler dev

wrangler init komutu size birkaç soru sorar: TypeScript mi JavaScript mi kullanmak istiyorsunuz, test framework istiyor musunuz gibi. Biz bu yazıda JavaScript ile devam edeceğiz çünkü sysadmin’lerin çoğu TypeScript toolchain’iyle uğraşmak istemez.

İlk Worker’ı Anlamak

Proje oluşturduktan sonra src/index.js dosyasını açın. Temel yapı şöyle görünür:

export default {
  async fetch(request, env, ctx) {
    return new Response("Merhaba Edge Dünyasi!", {
      headers: {
        "Content-Type": "text/plain; charset=utf-8",
      },
    });
  },
};

Bu yapıyı anlayalım:

  • request: Gelen HTTP isteği, URL, headers, body gibi her şeyi içerir
  • env: Ortam değişkenleri ve binding’ler burada yaşar (KV store, secrets vs.)
  • ctx: Context nesnesi, waitUntil gibi async operasyonlar için kullanılır

wrangler dev çalışıyorken http://localhost:8787 adresine gittiğinizde yanıtı görebilirsiniz. Her değişiklikte otomatik reload olur, klasik bir geliştirme deneyimi sunar.

Gerçek Dünya Senaryosu 1: Akıllı Redirect Yönetimi

En sık kullandığım Workers senaryolarından biri URL yönlendirmelerini edge’de yönetmek. Diyelim ki eski domain’inizden yeni domain’e geçtiniz ve yüzlerce redirect kuralınız var. Nginx’te bu kuralları yönetmek bir kabus olabilir, ancak Workers ile bunu merkezi bir JSON veya KV store’dan yönetebilirsiniz.

// src/index.js - Akilli redirect yoneticisi
const redirectMap = {
  "/eski-sayfa": "https://yenidomain.com/yeni-sayfa",
  "/blog/eski-kategori": "https://yenidomain.com/makaleler",
  "/urunler/eski": "https://yenidomain.com/magaza",
};

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const pathname = url.pathname;

    // Tam eslesen redirect var mi?
    if (redirectMap[pathname]) {
      return Response.redirect(redirectMap[pathname], 301);
    }

    // Prefix bazli redirect kontrolu
    if (pathname.startsWith("/eski-kategori/")) {
      const newPath = pathname.replace("/eski-kategori/", "/yeni-kategori/");
      return Response.redirect(`https://yenidomain.com${newPath}`, 301);
    }

    // Redirect yoksa isteği origin'e ilet
    return fetch(request);
  },
};

Bu yaklaşımın güzelliği şu: Redirect listesi değiştiğinde sunucuya dokunmuyorsunuz, sadece Worker’ı güncelleyip deploy ediyorsunuz. Deployment global olarak 30 saniyeden kısa sürede yayılıyor.

Gerçek Dünya Senaryosu 2: Rate Limiting ve Bot Koruması

Workers, IP bazlı rate limiting için mükemmel bir yer. Cloudflare zaten bot koruması yapıyor ama özel iş mantığınıza göre ek filtreleme yapmak isteyebilirsiniz. Bunun için Workers KV’yi kullanacağız.

Önce wrangler.toml dosyasına KV namespace ekleyelim:

# KV namespace olustur
wrangler kv:namespace create "RATE_LIMIT"

# Ciktidaki ID'yi wrangler.toml'a ekle
name = "my-edge-worker"
main = "src/index.js"
compatibility_date = "2024-01-01"

[[kv_namespaces]]
binding = "RATE_LIMIT"
id = "buraya_namespace_id_gelecek"
// Rate limiting worker
export default {
  async fetch(request, env, ctx) {
    const clientIP = request.headers.get("CF-Connecting-IP");
    const url = new URL(request.url);
    
    // Sadece API endpoint'lerini koruyalim
    if (!url.pathname.startsWith("/api/")) {
      return fetch(request);
    }

    const rateLimitKey = `ratelimit:${clientIP}`;
    const windowSeconds = 60;
    const maxRequests = 30;

    // Mevcut sayaci oku
    const currentCount = await env.RATE_LIMIT.get(rateLimitKey);
    const count = currentCount ? parseInt(currentCount) : 0;

    if (count >= maxRequests) {
      return new Response(
        JSON.stringify({
          error: "Rate limit asildi",
          message: "Lutfen 1 dakika bekleyin",
          retryAfter: windowSeconds,
        }),
        {
          status: 429,
          headers: {
            "Content-Type": "application/json",
            "Retry-After": windowSeconds.toString(),
            "X-RateLimit-Limit": maxRequests.toString(),
            "X-RateLimit-Remaining": "0",
          },
        }
      );
    }

    // Sayaci artir
    await env.RATE_LIMIT.put(rateLimitKey, (count + 1).toString(), {
      expirationTtl: windowSeconds,
    });

    // Isteği ilet ve rate limit headerları ekle
    const response = await fetch(request);
    const newResponse = new Response(response.body, response);
    newResponse.headers.set("X-RateLimit-Limit", maxRequests.toString());
    newResponse.headers.set(
      "X-RateLimit-Remaining",
      (maxRequests - count - 1).toString()
    );

    return newResponse;
  },
};

Bu Worker her 60 saniyede IP başına 30 istekle sınırlandırma yapıyor. KV’nin global replikasyonu sayesinde bu limit tüm edge lokasyonlarında geçerli oluyor.

Gerçek Dünya Senaryosu 3: Coğrafi Yönlendirme

Cloudflare Workers, her istekle birlikte coğrafi bilgi de sağlar. Kullanıcıyı ülkesine göre farklı bir sunucuya veya içeriğe yönlendirmek istiyorsanız bu bilgiyi kullanabilirsiniz.

// Cografik yonlendirme worker
export default {
  async fetch(request, env, ctx) {
    const country = request.cf?.country || "US";
    const url = new URL(request.url);

    // Yalnizca ana sayfaya uygulansin
    if (url.pathname !== "/") {
      return fetch(request);
    }

    // Ulke bazinda yonlendirme haritasi
    const regionMap = {
      TR: "https://tr.sirketim.com",
      DE: "https://de.sirketim.com",
      FR: "https://fr.sirketim.com",
      GB: "https://uk.sirketim.com",
    };

    // EU ulkeleri icin GDPR uyarisi sayfasi
    const euCountries = ["DE", "FR", "IT", "ES", "NL", "BE", "PL", "SE"];
    
    if (euCountries.includes(country)) {
      // EU kullanicisini GDPR uyumlu versiyona yonlendir
      const targetDomain = regionMap[country] || "https://eu.sirketim.com";
      return Response.redirect(targetDomain + url.pathname, 302);
    }

    if (regionMap[country]) {
      return Response.redirect(regionMap[country] + url.pathname, 302);
    }

    // Varsayilan: isteği doğrudan ilet
    return fetch(request);
  },
};

request.cf objesi içinde sadece ülke değil, şehir (city), koordinatlar (latitude, longitude), ASN (asn), ve hatta datacenter kodu (colo) gibi bilgiler de bulunur. Bu bilgiler ücretsiz olarak her istekte gelir.

Gerçek Dünya Senaryosu 4: Header Güvenlik Katmanı

Pek çok eski uygulama güvenlik header’larını düzgün ayarlamıyor. Bunu Workers’ta merkezi olarak yönetmek çok pratik.

// Guvenlik header yoneticisi
export default {
  async fetch(request, env, ctx) {
    const response = await fetch(request);
    
    // Orijinal response'u degistirilebilir hale getir
    const newResponse = new Response(response.body, {
      status: response.status,
      statusText: response.statusText,
      headers: response.headers,
    });

    // Guvenlik headerlarini ekle
    newResponse.headers.set(
      "Strict-Transport-Security",
      "max-age=31536000; includeSubDomains; preload"
    );
    newResponse.headers.set("X-Content-Type-Options", "nosniff");
    newResponse.headers.set("X-Frame-Options", "SAMEORIGIN");
    newResponse.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
    newResponse.headers.set(
      "Permissions-Policy",
      "camera=(), microphone=(), geolocation=(self)"
    );
    newResponse.headers.set(
      "Content-Security-Policy",
      "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline';"
    );

    // Sunucu bilgisini gizle
    newResponse.headers.delete("Server");
    newResponse.headers.delete("X-Powered-By");
    newResponse.headers.delete("X-AspNet-Version");

    return newResponse;
  },
};

Bu Worker’ı deploy ettikten sonra securityheaders.com gibi bir araçla sitenizi test edin. Birkaç satır kodla A+ rating almak mümkün.

Wrangler ile Deploy ve Yönetim

Geliştirme aşaması bitti, canlıya almak istiyorsunuz. Önce wrangler.toml dosyanızı düzenleyin:

name = "guvenlik-header-worker"
main = "src/index.js"
compatibility_date = "2024-01-15"

# Hangi route'lara uygulanacak
[[routes]]
pattern = "sirketim.com/*"
zone_name = "sirketim.com"

[[routes]]
pattern = "www.sirketim.com/*"
zone_name = "sirketim.com"

# Ortam degiskenleri
[vars]
ENVIRONMENT = "production"

# Secret'lar wrangler secret put ile eklenir, buraya yazilmaz
# Canli ortama deploy et
wrangler deploy

# Versiyonlari listele
wrangler deployments list

# Onceki versiyona don
wrangler rollback

# Worker loglarini canli izle
wrangler tail

# Secret ekle (hassas bilgiler icin)
wrangler secret put API_KEY

wrangler tail komutu gerçek zamanlı log izleme sağlar. Bir sorun çıktığında buradan hata mesajlarını, istek/yanıt bilgilerini görebilirsiniz. Benim en çok kullandığım debug aracı.

Ortam Yönetimi

Production’a direkt deploy etmek yerine staging ortamı kullanmak iyi bir pratik:

# wrangler.toml'a ortam ekle
[env.staging]
name = "guvenlik-header-worker-staging"
vars = { ENVIRONMENT = "staging" }

[[env.staging.routes]]
pattern = "staging.sirketim.com/*"
zone_name = "sirketim.com"

[env.production]
name = "guvenlik-header-worker"
vars = { ENVIRONMENT = "production" }

[[env.production.routes]]
pattern = "sirketim.com/*"
zone_name = "sirketim.com"
# Staging'e deploy et
wrangler deploy --env staging

# Test et, sonra production'a al
wrangler deploy --env production

Bu şekilde staging’de test ettikten sonra aynı kodu production’a alıyorsunuz. Bir sorun çıksa bile wrangler rollback ile önceki versiyona dönmek 10 saniye sürer.

Workers D1 ile Veritabanı Kullanımı

Bazı senaryolarda Workers’ın basit bir veritabanına erişmesi gerekir. D1, Cloudflare’in SQLite tabanlı edge veritabanıdır.

# D1 veritabani olustur
wrangler d1 create blog-db

# Tablo olustur
wrangler d1 execute blog-db --command "CREATE TABLE redirects (source TEXT PRIMARY KEY, destination TEXT NOT NULL, status_code INTEGER DEFAULT 301);"

# Veri ekle
wrangler d1 execute blog-db --command "INSERT INTO redirects VALUES ('/eski-yol', 'https://yenidomain.com/yeni-yol', 301);"

# wrangler.toml'a binding ekle
[[d1_databases]]
binding = "DB"
database_name = "blog-db"
database_id = "veritabani-id-buraya"
// D1 ile dinamik redirect yoneticisi
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    // Veritabanindan redirect kural sorgula
    const result = await env.DB.prepare(
      "SELECT destination, status_code FROM redirects WHERE source = ?"
    )
      .bind(url.pathname)
      .first();

    if (result) {
      return Response.redirect(result.destination, result.status_code);
    }

    return fetch(request);
  },
};

D1 ile artık redirect kurallarınızı bir admin arayüzünden yönetebilir, Workers’a dokunmadan ekleyip silebilirsiniz.

Performans ve Limitler

Workers kullanırken bazı limitleri bilmek gerekiyor:

  • CPU süresi: Ücretsiz planda istek başına 10ms CPU süresi, Workers Unbound’da bu limit kalkar
  • Bellek: Her Worker izolat’ı için 128MB
  • Script boyutu: 1MB (gzip sonrası)
  • KV yazma sınırı: Saniyede bir key için bir yazma işlemi
  • Subrequest limiti: İstek başına 50 fetch() çağrısı

Ücretsiz planda CPU süresi bazen kısıtlayıcı olabiliyor. Eğer Workers’ınız karmaşık işlemler yapıyorsa ve 10ms’ye takılıyorsa $5/ay olan Workers Paid planına geçmeyi düşünün. Aylık 10 milyon istek ve 30 milyon CPU milisaniyesi geliyor.

Birkaç performans ipucu:

  • await fetch() çağrılarını mümkünse paralel yapın: Promise.all([fetch(url1), fetch(url2)])
  • KV okumalarını minimize edin, aynı Worker içinde tekrarlanan key’leri cache’leyin
  • Response streaming kullanın, büyük yanıtları tamamen belleğe almayın
  • ctx.waitUntil() ile kritik olmayan işlemleri arka planda çalıştırın

Monitoring ve Alerting

Workers’ı production’da bırakıp unutmak olmaz. Cloudflare Dashboard’da Workers & Pages > Workers adresinden her Worker’ınızın metriklerini görebilirsiniz. Ama daha gelişmiş monitoring için:

# Logları bir syslog servise gonder
# wrangler.toml'a log sink ekle
[[tail_consumers]]
service = "log-worker"

Dashboard’dan Workers > Analytics altında şunları takip edin: istek sayısı, hata oranı (4xx ve 5xx), CPU süresi dağılımı. Özellikle hata oranı aniden yükselirse Cloudflare’in bildirim sistemiyle alert kurabilirsiniz: Notifications > Add Notification > Workers sekmesi altında “Workers Usage” veya “Workers Error Rate” alertleri eklenebilir.

CI/CD Pipeline ile Otomatik Deploy

GitHub Actions ile Workers deployment’ını otomatize etmek çok basit:

# GitHub repository secret'larına ekle:
# CLOUDFLARE_API_TOKEN = wrangler token
# CLOUDFLARE_ACCOUNT_ID = account ID'niz
# .github/workflows/deploy.yml
name: Deploy Workers

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to Cloudflare Workers
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: deploy --env production

Bu pipeline main branch’e push olduğunda otomatik deploy başlatır. 2-3 dakika içinde değişiklikleriniz tüm dünyaya yayılmış olur.

DNS Entegrasyonu

Workers ile Cloudflare DNS arasındaki entegrasyon gerçekten pürüzsüz. Bir Worker’ı belirli bir subdomain’e bağlamak için ya route tanımlaması ya da Workers domain özelliğini kullanırsınız.

Dashboard üzerinden yapmak isterseniz: Workers & Pages > Worker’ınız > Settings > Triggers > Add Custom Domain. Buraya api.sirketim.com yazdığınızda Cloudflare otomatik olarak bir DNS kaydı oluşturur ve SSL sertifikası sağlar. Siz sadece Worker’ı yazarsınız, geri kalanı otomatik hallolur.

Route tabanlı bağlama ise mevcut subdomain’in trafik akışını Workers’a yönlendirir ama origin sunucu varken bile araya girmesini sağlar. Bu hibrit kullanım çok güçlü: Bazı path’lere Workers ile müdahale ederken diğerleri doğrudan origin’e gider.

Sonuç

Cloudflare Workers, sysadmin araç kutusuna gerçekten değerli bir ekleme. Sunucu yönetmeden, ölçeklendirme planı yapmadan, global dağıtım derdine düşmeden edge’de mantık çalıştırabilmek hem zaman hem para tasarrufu sağlıyor.

En basit kullanım senaryolarından başlamak mantıklı: Önce bir redirect yöneticisi veya güvenlik header ekleme Worker’ı yazın. Platforma alışınca daha karmaşık senaryolara geçebilirsiniz. Rate limiting, A/B testing, authentication proxy, webhook router gibi konular sizi bekliyor.

Ücretsiz katman günlük 100.000 istek sunuyor, orta ölçekli bir proje için fazlasıyla yeterli. Ücretsiz Workers.dev subdomain ile production öncesi her şeyi test edebilir, tatmin olduktan sonra kendi domain’inize bağlayabilirsiniz.

Wrangler CLI’ı bir kez kurup birkaç Worker deploy ettikten sonra “bunu neden daha önce denememiştim” diyeceksiniz. Özellikle Cloudflare DNS zaten kullanıyorsanız, Workers’a geçiş neredeyse sıfır sürtünmeli. Denemesi ücretsiz, öğrenmesi birkaç saat, getirisi ise oldukça yüksek.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir