İçeriğe atla
Cloudflare Wiki

    ↑↓ gez↵ açEsc kapatCloudflare Wiki

    Workers

    JavaScript, TypeScript veya Python kodunu Cloudflare’in 330+ şehirdeki sunucularında, hiçbir sunucu yönetmeden ve kullanıcıya en yakın noktada çalıştırır.

    • DurumGenel kullanımda
    • Fiyatİstek sayısı + CPU süresi (bekleme süresi ücretsiz)
    • Ücretsiz katmanvar
    • Doğrulama
    Cloudflare Workers mimarisi: internetten gelen istek Worker'a ulaşır, Worker önbellek, KV ve R2 ile konuşur, gerekirse origin sunucuya gider

    Workers nedir?

    Bir web isteği normalde şu yolu izler: kullanıcının tarayıcısı → DNS → (varsa CDN) → senin sunucun. Uygulama kodun o sunucuda, tek bir yerde çalışır. İstanbul’daki kullanıcı da, Berlin’deki kullanıcı da aynı makineye ulaşmak zorundadır.

    Workers bu şemayı değiştirir: kodunu bir sunucuya değil, Cloudflare’in ağının kendisine yüklersin. Kod, isteği ilk karşılayan şehirde çalışır. İstanbul’dan gelen istek İstanbul’da, Frankfurt’tan gelen istek Frankfurt’ta işlenir. Ortada yönetmen gereken bir makine, kurman gereken bir işletim sistemi, yamalaman gereken bir çekirdek yoktur.

    Pratikte bu üç şey demektir:

    • Sunucu yönetimi ortadan kalkar. Kapasite planlaması, otomatik ölçekleme grubu, yük dengeleyici yapılandırması, güvenlik yaması yoktur. Kodu yayına alırsın, gerisi Cloudflare’in sorumluluğundadır.
    • Gecikme düşer. Kod kullanıcıya coğrafi olarak yakın çalıştığı için, origin sunucuna gitmeyen istekler tek haneli milisaniyelerde yanıtlanır.
    • Boşta durmak bedava olur. Fatura, çalışan koda göre kesilir. Gece hiç istek gelmeyen bir servis için ödeme yapmazsın.

    Nasıl çalışır?

    Workers’ın diğer sunucusuz platformlardan (AWS Lambda, Google Cloud Functions) ayrıldığı temel nokta, konteyner değil izolat (isolate) kullanmasıdır.

    Klasik sunucusuz platformlar her fonksiyon için bir konteyner veya mikro-sanal makine başlatır. Bu birkaç yüz milisaniyeden birkaç saniyeye kadar süren bir soğuk başlangıç (cold start) demektir. Workers ise Chrome’un JavaScript motoru olan V8’in “isolate” mekanizmasını kullanır. Bir isolate, aynı süreç içinde çalışan ama belleği tamamen ayrılmış hafif bir bağlamdır; kurulumu milisaniyenin altında sürer.

    Bir isteğin ömrü şöyledir:

    1. İstek, kullanıcıya en yakın Cloudflare şehrine ulaşır.
    2. Cloudflare, o alan adına bağlı Worker’ı bulur ve bir isolate içinde fetch fonksiyonunu çağırır.
    3. Kod çalışır: gelen isteği okur, gerekirse KV’den veri okur, gerekirse origin sunucuna fetch atar, yanıtı oluşturur.
    4. Yanıt kullanıcıya döner. Worker isterse arka planda iş yapmaya devam eder (waitUntil).

    En basit haliyle bir Worker şudur:

    export default {
      async fetch(request, env, ctx) {
        return new Response("Merhaba İstanbul", {
          headers: { "content-type": "text/plain; charset=utf-8" },
        });
      },
    };

    Bağlantılar (bindings)

    Worker’ın dış dünyaya erişimi env nesnesi üzerinden, binding denen tanımlarla olur. Kodun içine bağlantı dizesi veya API anahtarı gömmezsin: wrangler.jsonc dosyasında hangi kaynağa erişeceğini bildirirsin, Cloudflare o kaynağı çalışma anında env içine koyar.

    export default {
      async fetch(request, env) {
        // Durable Object — adı verilen tek bir nesneye bağlan
        const id = env.GAME_ROOM.idFromName("oda-42");
        const oda = env.GAME_ROOM.get(id);
    
        // D1 — sunucusuz SQL
        const { results } = await env.DB.prepare(
          "select ad, puan from oyuncular order by puan desc limit 10",
        ).all();
    
        // R2 — nesne depolama
        const logo = await env.BRAND.get("logo.svg");
    
        return Response.json({ liderler: results });
      },
    };

    Yayına alırken wrangler, Worker’ın hangi kaynaklara bağlı olduğunu listeler. Aşağıdaki demo bölümünde bunun gerçek çıktısı var: solda kodun gördüğü ad (env.DB), sağda arkasındaki kaynak (D1 Database).

    Hata ayıklarken ilk bakılacak yer burasıdır: beklediğin bağlantı listede görünmüyorsa wrangler.jsonc düzenlenmiş ama yayına alınmamış demektir.

    CPU süresi ile bekleme süresi farkı

    Bu ayrım, Workers’ın faturasını anlamanın anahtarıdır ve yeni başlayanların en sık yanlış hesapladığı yerdir.

    • CPU süresi: İşlemcinin gerçekten senin kodunu çalıştırdığı süre. JSON ayrıştırma, imza doğrulama, şablon işleme.
    • Bekleme süresi (duration / wall-clock): Kodun bir yanıt beklediği süre. Origin sunucundan cevap beklemek, veritabanı sorgusunun dönmesini beklemek.

    Cloudflare yalnızca CPU süresini ücretlendirir. Origin sunucun 3 saniyede cevap veriyorsa, Worker o 3 saniye boyunca bekler ama sen o beklemeye para ödemezsin. Ödediğin, isteği hazırlamak ve yanıtı işlemek için harcanan birkaç milisaniyedir.

    Cloudflare paneli bu iki değeri yan yana gösterir. Demo bölümündeki metrik ekranında gerçek bir ölçüm var: CPU Time 865 µs, Wall Time 19 ms — yaklaşık 22 kat fark. Faturaya giren yalnızca soldaki.

    Ne zaman kullanılır, ne zaman kullanılmaz

    Bir referans kaynağının işi, ürünü satmak değil sınırlarını göstermektir. Workers her iş için doğru araç değildir.

    Uygun olduğu işler

    • Origin’in önünde kimlik doğrulama, yetkilendirme, token doğrulama
    • A/B testi, kademeli yayın (canary), bakım modu, coğrafi yönlendirme
    • İstek/yanıt dönüştürme: başlık ekleme, HTML enjeksiyonu, eski API’yi yeni şemaya çevirme
    • Webhook karşılama ve kuyruğa yazma
    • API birleştirme (birden çok iç servisi tek yanıtta toplama)
    • Statik site + hafif API (Pages ile birlikte)
    • Bot filtreleme, hız sınırlama, imzalı URL üretme

    Uygun olmadığı işler

    • Uzun süren, CPU yoğun hesaplama. Video kodlama, büyük veri işleme, makine öğrenmesi eğitimi. Çağrı başına CPU süresi üst sınırı vardır (ücretsizde 10 ms, ücretlide varsayılan 30 sn, en fazla 5 dk).
    • 128 MB’dan fazla bellek isteyen işler. İzolat başına bellek sınırı sabittir ve ücretli planda da artmaz.
    • Native ikili bağımlılık gerektiren kod. ImageMagick, ffmpeg, kendi derlediğin C kütüphanesi. Bunlar için Containers vardır.
    • Uzun ömürlü, havuzlanmış veritabanı bağlantıları. Ham TCP mümkündür (cloudflare:sockets) ama her çağrıda yeniden kurulum turu ödersin; PostgreSQL/MySQL için Hyperdrive kullan.
    • Disk üzerinde dosya sistemi bekleyen kod. Worker’ın yazılabilir bir diski yoktur; kalıcı veri için R2, KV, D1 veya Durable Objects kullanılır.

    Somut örnekler

    1. Şirket içi API’nin önüne kimlik katmanı

    Kendi veri merkezindeki bir API’yi internete açtın. Her isteğin geçerli bir imza taşımasını istiyorsun ve geçersiz isteklerin hiç iç ağa girmemesini tercih ediyorsun.

    export default {
      async fetch(request, env) {
        const imza = request.headers.get("x-imza");
        const zaman = request.headers.get("x-zaman");
    
        if (!imza || !zaman) {
          return yanit(401, "İmza başlıkları eksik");
        }
    
        // 5 dakikadan eski istekleri tekrar oynatma saldırısına karşı reddet
        const yas = Math.abs(Date.now() - Number(zaman));
        if (!Number.isFinite(yas) || yas > 5 * 60 * 1000) {
          return yanit(401, "İstek zaman aşımına uğradı");
        }
    
        const govde = await request.clone().text();
        if (!(await imzaDogru(env.PAYLASILAN_SIR, zaman + govde, imza))) {
          return yanit(403, "İmza geçersiz");
        }
    
        // Doğrulandı: isteği Cloudflare Tunnel üzerinden iç API'ye ilet
        return fetch(request);
      },
    };
    
    async function imzaDogru(sir, veri, beklenenHex) {
      const enc = new TextEncoder();
      const anahtar = await crypto.subtle.importKey(
        "raw",
        enc.encode(sir),
        { name: "HMAC", hash: "SHA-256" },
        false,
        ["verify"],
      );
      const bayt = Uint8Array.from(
        beklenenHex.match(/.{1,2}/g) ?? [],
        (b) => parseInt(b, 16),
      );
      return crypto.subtle.verify("HMAC", anahtar, bayt, enc.encode(veri));
    }
    
    function yanit(kod, mesaj) {
      return new Response(JSON.stringify({ hata: mesaj }), {
        status: kod,
        headers: { "content-type": "application/json; charset=utf-8" },
      });
    }

    Kazanç: geçersiz istekler İstanbul’daki Cloudflare sunucusunda ölür, senin hattını ve sunucunu hiç meşgul etmez.

    2. Bakım modu — origin’e dokunmadan

    Sunucuda planlı bakım var. Uygulamayı durdurmadan, kullanıcıya düzgün bir sayfa göstermek istiyorsun.

    export default {
      async fetch(request, env) {
        // BAKIM değişkeni Cloudflare panelinden anında açılıp kapanır
        if (env.BAKIM === "acik") {
          const ip = request.headers.get("cf-connecting-ip");
          const ofisIp = ["203.0.113.10"]; // ofis çıkışı bakımda da erişebilsin
    
          if (!ofisIp.includes(ip)) {
            return new Response(BAKIM_HTML, {
              status: 503,
              headers: {
                "content-type": "text/html; charset=utf-8",
                "retry-after": "1800",
                "cache-control": "no-store",
              },
            });
          }
        }
        return fetch(request);
      },
    };
    
    const BAKIM_HTML = `<!doctype html><html lang="tr"><meta charset="utf-8">
    <title>Bakım çalışması</title>
    <h1>Kısa bir bakım çalışması yapıyoruz</h1>
    <p>Yaklaşık 30 dakika içinde geri döneceğiz.</p>`;

    503 ve retry-after başlığı önemlidir: arama motorları bunu geçici durum olarak okur, sıralamanı düşürmez.

    3. Coğrafi yönlendirme

    Cloudflare her isteğe request.cf nesnesiyle ülke, şehir ve veri merkezi bilgisi ekler. Ek bir GeoIP veritabanı kurmana gerek yoktur.

    export default {
      async fetch(request) {
        const ulke = request.cf?.country ?? "XX";
        const url = new URL(request.url);
    
        // Türkiye'den gelen ve dil seçmemiş kullanıcıyı /tr/ altına al
        if (ulke === "TR" && url.pathname === "/") {
          return Response.redirect(new URL("/tr/", url), 302);
        }
        return fetch(request);
      },
    };

    4. Yanıtı kenarda önbellekleme

    Origin’in ürettiği pahalı bir yanıtı Cloudflare’in kendi önbelleğinde tutarsın.

    export default {
      async fetch(request, env, ctx) {
        const onbellek = caches.default;
        const vurus = await onbellek.match(request);
        if (vurus) return vurus;
    
        const yanit = await fetch(request);
        const kopya = new Response(yanit.body, yanit);
        kopya.headers.set("cache-control", "public, max-age=300");
    
        // Yanıtı kullanıcıya hemen gönder, önbelleğe yazmayı arka planda bitir
        ctx.waitUntil(onbellek.put(request, kopya.clone()));
        return kopya;
      },
    };

    ctx.waitUntil burada kritiktir: yanıt kullanıcıya gecikmeden gider, önbelleğe yazma işlemi arka planda tamamlanır.

    Demo 1: İç ERP’nin stok servisini internete hiç açmadan yayına almak

    Senaryo. Bir e-ticaret şirketinin stok bilgisi şirket içindeki ERP sunucusunda duruyor. Mobil uygulama bu bilgiyi görmek istiyor. Klasik çözüm sunucuya bir port açıp önüne bir reverse proxy koymak — ama o sunucu muhasebe verisini de tutuyor ve internete bakan hiçbir portu olmaması gerekiyor.

    Kuracağımız mimaride ERP sunucusunun hiçbir gelen portu açılmıyor. Servis yalnızca 127.0.0.1 üzerinde dinliyor, dışarıya çıkışı cloudflared sağlıyor, ve internete bakan tek yüz Cloudflare’de çalışan bir Worker.

    Mobil uygulama
          │  https://api.ornek.com/stok/ABC-123   + HMAC imzası
          ▼
    Cloudflare Worker  ── imza doğrula → 401 ise burada biter
          │             ── cache’te varsa buradan dön (origin’e hiç gidilmez)
          │             ── iç başlıkları temizle
          ▼  https://ic-api.ornek.com
    Cloudflare Tunnel
          ▼
    ERP sunucusu  →  127.0.0.1:8080   (dışarıdan erişilemez)

    Worker’ın burada yaptığı üç iş, origin’de yapılamayacak veya yapılması pahalı olan işler: imzasız istek ERP’ye hiç ulaşmıyor, tekrarlanan sorgular kullanıcıya en yakın şehirde yanıtlanıyor, ve ERP’nin sızdırdığı iç başlıklar dışarı çıkmıyor.

    Adım 1 — İç servisi ayağa kaldır

    ERP’nin gerçek API’si yerine, davranışı aynı olan küçük bir servis kullanıyoruz. Bağımlılık gerekmiyor, Ubuntu’da hazır gelen Python yeter.

    # ~/stok-api/sunucu.py
    import json
    from http.server import BaseHTTPRequestHandler, HTTPServer
    
    STOK = {
        "ABC-123": {"ad": "Kablosuz klavye", "adet": 42, "depo": "Istanbul-1"},
        "XYZ-900": {"ad": "USB-C kablo 2m", "adet": 0, "depo": "Izmir-2"},
    }
    
    class Kolcu(BaseHTTPRequestHandler):
        def do_GET(self):
            if not self.path.startswith("/stok/"):
                return self.yaz(404, {"hata": "bilinmeyen-yol"})
            sku = self.path[len("/stok/"):]
            kayit = STOK.get(sku)
            if kayit is None:
                return self.yaz(404, {"hata": "sku-yok", "sku": sku})
            self.yaz(200, {"sku": sku, **kayit})
    
        def yaz(self, durum, govde):
            veri = json.dumps(govde, ensure_ascii=False).encode()
            self.send_response(durum)
            self.send_header("content-type", "application/json; charset=utf-8")
            self.send_header("content-length", str(len(veri)))
            # ERP'lerin sizdirdigi tipik ic baslik — Worker'da temizleyecegiz
            self.send_header("x-erp-sunucu", "erp-prod-02.sirket.local")
            self.end_headers()
            self.wfile.write(veri)
    
        def log_message(self, bicim, *args):
            print(f"[ic-api] {bicim % args}")
    
    # DIKKAT: 0.0.0.0 degil. Servis ag kartina hic baglanmiyor.
    HTTPServer(("127.0.0.1", 8080), Kolcu).serve_forever()
    mkdir -p ~/stok-api && cd ~/stok-api
    python3 sunucu.py &
    curl -s http://127.0.0.1:8080/stok/ABC-123

    Servisin gerçekten dışarıya kapalı olduğunu kanıtla — bu demonun bütün noktası bu:

    # Sunucunun kendi LAN IP'sinden dene: baglanti reddedilmeli
    ip -4 addr show | grep inet
    curl -sS --max-time 5 http://192.168.70.234:8080/stok/ABC-123 ; echo "çıkış kodu: $?"
    
    # Dinleyen soketi göster: yalnızca 127.0.0.1:8080 görünmeli
    ss -tlnp | grep 8080
    Terminal — üstte localhost'tan gelen başarılı JSON yanıtı, altta LAN IP'sine yapılan curl'ün 'Connection refused' ile düşmesi ve ss -tlnp çıktısında 127.0.0.1:8080 satırı

    Adım 2 — Tüneli kur

    cloudflared yalnızca dışarı doğru bağlantı açar; güvenlik duvarında hiçbir gelen kural değişmez.

    curl -L -o cloudflared.deb \
      https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
    sudo dpkg -i cloudflared.deb
    cloudflared --version
    
    cloudflared tunnel login                    # tarayıcıda zone seçilir
    cloudflared tunnel create erp-stok
    cloudflared tunnel route dns erp-stok ic-api.ornek.com
    # ~/.cloudflared/config.yml
    tunnel: erp-stok
    credentials-file: /home/samet/.cloudflared/<TUNEL-ID>.json
    
    ingress:
      - hostname: ic-api.ornek.com
        service: http://127.0.0.1:8080
      - service: http_status:404
    cloudflared tunnel ingress validate
    sudo cloudflared service install
    sudo systemctl status cloudflared --no-pager
    Terminal — systemctl status cloudflared çıktısı (active/running) ve log satırlarında 'Registered tunnel connection' geçen dört bağlantı, her birinin bağlandığı şehir kodu görünecek şekilde

    Adım 3 — Worker’ı yaz

    npm create cloudflare@latest -- stok-kapisi \
      --type=hello-world --lang=js --git=false --deploy=false
    cd stok-kapisi
    // wrangler.jsonc
    {
      "name": "stok-kapisi",
      "main": "src/index.js",
      "compatibility_date": "2026-08-31",
      "observability": { "enabled": true },
      "vars": { "IC_API": "https://ic-api.ornek.com" },
      "routes": [{ "pattern": "api.ornek.com/stok/*", "zone_name": "ornek.com" }]
    }

    Paylaşılan sır kod deposuna girmez ve iki ayrı yere yazılır. Bu, demoda en sık takılınan nokta: wrangler secret put yayındaki Worker’a yazar, wrangler dev ise local workerd çalışma zamanını kullanır ve o secret’ı görmez.

    # 1) Yerelde calistirmak icin — .dev.vars, proje kokunde
    cat > .dev.vars <<'EOF'
    PAYLASILAN_SIR="demo-sir-degistir"
    EOF
    echo ".dev.vars" >> .gitignore
    
    # 2) Yayina alirken — Cloudflare tarafina
    npx wrangler secret put PAYLASILAN_SIR

    Değer, birazdan yazacağın istemci.sh içindeki SIR ile birebir aynı olmalı.

    // src/index.js
    const AZAMI_SAPMA_SN = 300;   // 5 dakikadan eski imza kabul edilmez
    const ONBELLEK_SN = 30;
    
    export default {
      async fetch(request, env, ctx) {
        const url = new URL(request.url);
    
        if (request.method !== "GET") return json({ hata: "yalnizca-get" }, 405);
        if (!url.pathname.startsWith("/stok/")) return json({ hata: "bilinmeyen-yol" }, 404);
    
        // 0) Sir yoksa erken ve anlasilir bicimde dur.
        //    Yerelde .dev.vars, yayinda `wrangler secret put` unutulunca buraya duser.
        if (!env.PAYLASILAN_SIR) return json({ hata: "sir-tanimsiz" }, 500);
    
        // 1) İmza — geçersizse ERP'ye hiç gidilmez
        const dogrulama = await imzaDogrula(request, url.pathname, env.PAYLASILAN_SIR);
        if (!dogrulama.ok) return json({ hata: dogrulama.sebep }, 401);
    
        // 2) Önbellek. İmza her istekte değiştiği için anahtar imzasız URL'den üretilir.
        const anahtar = new Request(url.origin + url.pathname, { method: "GET" });
        const onbellek = await caches.default.match(anahtar);
        if (onbellek) {
          const y = new Response(onbellek.body, onbellek);
          y.headers.set("x-onbellek", "HIT");
          return y;
        }
    
        // 3) Tünelin arkasındaki iç servis
        let ustAkis;
        try {
          ustAkis = await fetch(new URL(url.pathname, env.IC_API), {
            signal: AbortSignal.timeout(5000),
          });
        } catch {
          return json({ hata: "ust-akis-ulasilamiyor" }, 504);
        }
        if (!ustAkis.ok) {
          return json({ hata: "ust-akis", durum: ustAkis.status }, ustAkis.status === 404 ? 404 : 502);
        }
    
        // 4) Yanıtı yeniden kur — ERP'nin iç başlıkları dışarı çıkmasın
        const cikti = new Response(await ustAkis.text(), {
          status: 200,
          headers: {
            "content-type": "application/json; charset=utf-8",
            "cache-control": `public, max-age=${ONBELLEK_SN}`,
            "x-onbellek": "MISS",
          },
        });
        ctx.waitUntil(caches.default.put(anahtar, cikti.clone()));
        return cikti;
      },
    };
    
    async function imzaDogrula(request, yol, sir) {
      const imza = request.headers.get("x-imza");
      const zaman = request.headers.get("x-zaman");
      if (!imza || !zaman) return { ok: false, sebep: "imza-eksik" };
    
      const sapma = Math.abs(Math.floor(Date.now() / 1000) - Number(zaman));
      if (!Number.isFinite(sapma) || sapma > AZAMI_SAPMA_SN) return { ok: false, sebep: "zaman-asimi" };
    
      const anahtar = await crypto.subtle.importKey(
        "raw", new TextEncoder().encode(sir),
        { name: "HMAC", hash: "SHA-256" }, false, ["sign"],
      );
      const ham = await crypto.subtle.sign("HMAC", anahtar, new TextEncoder().encode(`${zaman}.${yol}`));
      const beklenen = [...new Uint8Array(ham)].map((b) => b.toString(16).padStart(2, "0")).join("");
    
      // Sabit zamanlı karşılaştırma — erken çıkış zamanlama sızıntısı yaratır
      if (beklenen.length !== imza.length) return { ok: false, sebep: "imza-gecersiz" };
      let fark = 0;
      for (let i = 0; i < beklenen.length; i++) fark |= beklenen.charCodeAt(i) ^ imza.charCodeAt(i);
      return fark === 0 ? { ok: true } : { ok: false, sebep: "imza-gecersiz" };
    }
    
    const json = (govde, durum) => Response.json(govde, { status: durum });

    Adım 4 — Yerelde çalıştır

    wrangler dev gerçek workerd çalışma zamanını kullanır; kod yayındaki ile aynı motorda döner.

    npx wrangler dev

    Başka bir terminalde istemciyi taklit et:

    # ~/stok-api/istemci.sh
    SIR="demo-sir-degistir"          # wrangler secret put ile yazdığın değerin aynısı
    ADRES="${1:-http://localhost:8787}"
    YOL="${2:-/stok/ABC-123}"
    ZAMAN=$(date +%s)
    IMZA=$(printf '%s.%s' "$ZAMAN" "$YOL" | openssl dgst -sha256 -hmac "$SIR" -hex | awk '{print $NF}')
    curl -si -H "x-zaman: $ZAMAN" -H "x-imza: $IMZA" "$ADRES$YOL"
    chmod +x istemci.sh
    ./istemci.sh                                   # 200 + x-onbellek: MISS
    ./istemci.sh                                   # 200 + x-onbellek: HIT
    curl -si http://localhost:8787/stok/ABC-123    # 401 imza-eksik
    Terminal, iki bölmeli — solda wrangler dev çıktısı ve istek log'ları, sağda üç curl sonucu: MISS'li 200, HIT'li 200 ve {“hata”: “imza-eksik”} içeren 401

    Adım 5 — Yayına al

    npx wrangler deploy
    wrangler deploy çıktısı: yüklenen boyut, başlangıç süresi ve bağlantı listesi

    Canlıda aynı üç testi tekrarla:

    ./istemci.sh https://api.ornek.com
    ./istemci.sh https://api.ornek.com                      # ikinci istek HIT olmalı
    ./istemci.sh https://api.ornek.com /stok/YOK-000        # 404 sku-yok
    curl -si https://api.ornek.com/stok/ABC-123             # 401
    curl -si https://api.ornek.com/stok/ABC-123 | grep -i x-erp-sunucu   # boş dönmeli

    Son satır önemli: ERP’nin gönderdiği x-erp-sunucu başlığı Worker’da yeniden kurulan yanıtta yok. İç sunucu adı dışarı sızmıyor.

    Terminal — canlı adrese yapılan dört isteğin çıktısı; x-erp-sunucu araması sonuçsuz dönmeli (bu boş satır ekran görüntüsünde görünsün)

    Adım 6 — Önbelleğin gerçekten çalıştığını ölç

    # Ham gecikme farkı: MISS ile HIT arasında
    for i in 1 2 3 4 5; do
      curl -s -o /dev/null -w '%{http_code} %{time_total}s\n' \
        -H "x-zaman: $(date +%s)" \
        -H "x-imza: $(printf '%s.%s' "$(date +%s)" /stok/ABC-123 | openssl dgst -sha256 -hmac "$SIR" -hex | awk '{print $NF}')" \
        https://api.ornek.com/stok/ABC-123
    done

    Aynı anda ERP sunucusunda sunucu.py çıktısını izle: ilk istekten sonra [ic-api] satırı gelmemeli. İstek Cloudflare’de karşılanıyor, sunucuna hiç ulaşmıyor.

    İki bölmeli terminal — solda beş curl'ün HTTP kodu ve süresi (ilki belirgin şekilde yavaş), sağda sunucu.py çıktısında yalnızca tek bir [ic-api] satırı
    Cloudflare panelinde Worker metrikleri: çağrı sayısı, alt istek sayısı, hata oranı, CPU ve Wall Time

    Adım 7 — Hata yolunu da gör

    Demoyu değerli kılan kısım burası: bozulduğunda ne olduğunu görmek.

    # ERP servisini durdur
    pkill -f sunucu.py
    
    # Önbellek süresi dolduktan sonra iste
    sleep 35 && ./istemci.sh https://api.ornek.com

    Worker 504 ust-akis-ulasilamiyor döner — Cloudflare’in genel hata sayfası değil, senin yazdığın JSON. wrangler tail ile canlı log’a bak:

    npx wrangler tail --format pretty
    İki bölmeli terminal — solda 504 ve {“hata”: “ust-akis-ulasilamiyor”} yanıtı, sağda wrangler tail çıktısında aynı isteğin göründüğü satır

    Ölçüm tablosu

    Demoyu çalıştırdıktan sonra doldur:

    ÖlçütDeğer
    ERP sunucusunda açılan gelen port sayısı
    İmzasız isteğin ERP’ye ulaşma oranı
    İlk istek (MISS) süresi
    Sonraki istek (HIT) süresi
    5 istekte ERP’ye giden istek sayısı
    Worker CPU süresi (panelden)
    Dışarı sızan iç başlık sayısı

    Demo 2: Eski sunucudan yeni sunucuya kesintisiz geçiş

    Senaryo. Şirket uygulamayı eski fiziksel sunucudan yeni sunucuya taşıyor. DNS’i tek seferde çevirmek riskli: yeni sunucu yük altında nasıl davranacak bilinmiyor, ve DNS TTL yüzünden geri dönmek dakikalar sürüyor.

    Worker ile trafiği yüzde yüzde taşıyabilir, aynı kullanıcıyı hep aynı sunucuda tutabilir ve sorun çıkarsa tek bir değişkeni değiştirip saniyeler içinde geri dönebilirsin. DNS’e hiç dokunulmaz.

    Ek olarak gölge istek (shadow request) kuralım: her isteğin bir kopyası yeni sunucuya da gönderilir ama yanıtı kullanıcıya verilmez — yalnızca iki sunucunun aynı cevabı verip vermediği karşılaştırılır. Bu, geçişten önce sessizce doğrulama yapmanın yolu.

                     ┌─ %90 ─→ eski.ornek.com  (kullanıcıya dönen yanıt)
    Worker ─ çerez ──┤
                     └─ %10 ─→ yeni.ornek.com
                                  ▲
                     gölge istek ─┘  (yanıt atılır, yalnızca karşılaştırılır)

    Adım 1 — İki origin’i ayağa kaldır

    Aynı Ubuntu sunucusunda iki port yeterli. Her biri kendi adını dönsün ki hangisinin yanıtladığı görülebilsin.

    mkdir -p ~/gecis/eski ~/gecis/yeni
    echo '{"surum":"eski","sunucu":"erp-prod-01","ozellik":false}' > ~/gecis/eski/index.json
    echo '{"surum":"yeni","sunucu":"erp-prod-02","ozellik":true}'  > ~/gecis/yeni/index.json
    
    cd ~/gecis/eski && python3 -m http.server 8081 &
    cd ~/gecis/yeni && python3 -m http.server 8082 &

    Tünele iki hostname ekle:

    # ~/.cloudflared/config.yml
    ingress:
      - hostname: eski.ornek.com
        service: http://127.0.0.1:8081
      - hostname: yeni.ornek.com
        service: http://127.0.0.1:8082
      - hostname: ic-api.ornek.com
        service: http://127.0.0.1:8080
      - service: http_status:404
    cloudflared tunnel route dns erp-stok eski.ornek.com
    cloudflared tunnel route dns erp-stok yeni.ornek.com
    sudo systemctl restart cloudflared
    Terminal — eski.ornek.com ve yeni.ornek.com adreslerine yapılan iki curl; biri surum:eski, diğeri surum:yeni JSON'u dönüyor

    Adım 2 — Geçiş Worker’ı

    // wrangler.jsonc
    {
      "name": "kademeli-gecis",
      "main": "src/index.js",
      "compatibility_date": "2026-08-31",
      "observability": { "enabled": true },
      "vars": {
        "ESKI": "https://eski.ornek.com",
        "YENI": "https://yeni.ornek.com",
        "YENI_YUZDE": "10",
        "GOLGE": "true"
      }
    }
    // src/index.js
    const CEREZ = "gecis_grubu";
    
    export default {
      async fetch(request, env, ctx) {
        const yuzde = Number(env.YENI_YUZDE) || 0;
    
        // 1) Grup: çerez varsa ona sadık kal — aynı kullanıcı oturum boyunca aynı sunucuda kalır
        let grup = cerezOku(request, CEREZ);
        let cerezYaz = false;
        if (grup !== "eski" && grup !== "yeni") {
          grup = Math.random() * 100 < yuzde ? "yeni" : "eski";
          cerezYaz = true;
        }
    
        // 2) Acil kaçış: ?gecis=eski ile her zaman eskiye zorlanabilir
        const zorla = new URL(request.url).searchParams.get("gecis");
        if (zorla === "eski" || zorla === "yeni") grup = zorla;
    
        const hedef = grup === "yeni" ? env.YENI : env.ESKI;
        const istek = yenidenYaz(request, hedef);
    
        // 3) Gölge istek: kullanıcı eskide kaldıysa yeniye de sessizce sor
        if (env.GOLGE === "true" && grup === "eski") {
          ctx.waitUntil(golgeKarsilastir(request, env, istek));
        }
    
        const yanit = new Response((await fetch(istek)).body, await fetch(istek));
        const cikti = new Response(yanit.body, yanit);
        cikti.headers.set("x-grup", grup);
        if (cerezYaz) {
          cikti.headers.append(
            "set-cookie",
            `${CEREZ}=${grup}; Path=/; Max-Age=86400; Secure; HttpOnly; SameSite=Lax`,
          );
        }
        return cikti;
      },
    };
    
    function yenidenYaz(request, hedef) {
      const u = new URL(request.url);
      const h = new URL(hedef);
      u.hostname = h.hostname;
      u.protocol = h.protocol;
      return new Request(u, request);
    }
    
    function cerezOku(request, ad) {
      const ham = request.headers.get("cookie") || "";
      for (const parca of ham.split(";")) {
        const [k, v] = parca.trim().split("=");
        if (k === ad) return v;
      }
      return null;
    }
    
    async function golgeKarsilastir(request, env, asilIstek) {
      try {
        const golge = yenidenYaz(request, env.YENI);
        const [a, b] = await Promise.all([fetch(asilIstek), fetch(golge)]);
        const [x, y] = await Promise.all([a.text(), b.text()]);
        if (a.status !== b.status || x !== y) {
          console.log(JSON.stringify({
            olay: "golge-fark",
            yol: new URL(request.url).pathname,
            eskiDurum: a.status,
            yeniDurum: b.status,
            esit: x === y,
          }));
        }
      } catch (e) {
        console.log(JSON.stringify({ olay: "golge-hata", mesaj: String(e) }));
      }
    }

    Adım 3 — Dağılımı ölç

    npx wrangler deploy
    
    # Çerezleri yok sayarak 200 istek at, grupların dağılımına bak
    for i in $(seq 1 200); do
      curl -s -o /dev/null -D - https://uygulama.ornek.com/index.json | grep -i '^x-grup'
    done | sort | uniq -c

    Beklenen: yaklaşık 180 x-grup: eski ve 20 x-grup: yeni.

    Terminal — uniq -c çıktısında x-grup: eski ve x-grup: yeni satırlarının sayıları; %10 ayarına yakın bir dağılım görünecek

    Çerez yapışkanlığını doğrula — aynı oturum hep aynı grupta kalmalı:

    curl -s -c kavanoz.txt -o /dev/null -D - https://uygulama.ornek.com/index.json | grep -i 'x-grup\|set-cookie'
    for i in 1 2 3 4 5; do
      curl -s -b kavanoz.txt -o /dev/null -D - https://uygulama.ornek.com/index.json | grep -i '^x-grup'
    done
    Terminal — ilk istekte set-cookie: gecis_grubu=... satırı, ardından çerezle yapılan beş isteğin hepsinde aynı x-grup değeri

    Adım 4 — Gölge farkını yakala

    İki origin’in içeriğini bilerek ayır ve wrangler tail ile farkı gör:

    echo '{"surum":"yeni","sunucu":"erp-prod-02","ozellik":true,"fazladan":"alan"}' > ~/gecis/yeni/index.json
    npx wrangler tail --format pretty

    Başka bir terminalden birkaç istek at. golge-fark olayının log’a düşmesi gerekir.

    Terminal — wrangler tail penceresinde olay: “golge-fark” içeren JSON log satırı, eskiDurum ve yeniDurum alanları okunacak şekilde

    Adım 5 — Geri dönüşü ölç

    Bu, DNS ile geçiş yapmaya göre kazandığın asıl şey. Yüzdeyi sıfıra çek ve ne kadar sürdüğünü ölç:

    sed -i 's/"YENI_YUZDE": "10"/"YENI_YUZDE": "0"/' wrangler.jsonc
    
    date +%s.%N
    npx wrangler deploy
    date +%s.%N
    
    # Doğrula: artık hiçbir istek yeniye gitmemeli
    for i in $(seq 1 50); do
      curl -s -o /dev/null -D - https://uygulama.ornek.com/index.json | grep -i '^x-grup'
    done | sort | uniq -c
    Terminal — deploy komutunun öncesindeki ve sonrasındaki zaman damgaları ile sonrasında yapılan 50 isteğin tamamının x-grup: eski dönmesi

    Ölçüm tablosu

    ÖlçütDeğer
    200 istekte yeni sunucuya giden oran
    Çerezli 5 istekte grup değişimi
    Yakalanan golge-fark olayı sayısı
    %10 → %0 geri dönüş süresi
    DNS TTL ile aynı geri dönüş ne sürerdi
    Çift fetch hatası düzeltilmeden önce origin’e düşen istek sayısı
    Düzeltildikten sonra

    Demo 3: Statik site + API tek Worker’da

    Bu demo, Cloudflare’in yeni projeler için Pages yerine Workers önermesinin somut karşılığını gösterir: aynı Worker hem statik dosyaları sunar hem API’yi çalıştırır.

    Adım 1 — Projeyi oluştur

    npm create cloudflare@latest -- uygulamam --framework=react
    {
      "name": "uygulamam",
      "main": "src/worker.js",
      "compatibility_date": "2026-08-31",
      "observability": { "enabled": true },
      "assets": {
        "directory": "./dist",
        "binding": "ASSETS",
        "not_found_handling": "single-page-application"
      }
    }
    export default {
      async fetch(request, env) {
        const url = new URL(request.url);
        if (url.pathname.startsWith("/api/")) {
          return Response.json({ saat: new Date().toISOString() });
        }
        // Statik dosya bulunamazsa index.html dönsün (SPA)
        return env.ASSETS.fetch(request);
      },
    };
    Terminal — bindings listesinde ASSETS satırı ve http://localhost:8787 adresi (Pages'in 8788'i değil)

    Adım 2 — Yönlendirme sırasını doğrula

    curl -s http://localhost:8787/api/saat        # Worker çalışır
    curl -sI http://localhost:8787/               # statik dosya döner
    curl -sI http://localhost:8787/derin/yol      # SPA: index.html döner
    Üç curl çıktısı — API yanıtı, statik dosya başlıkları ve SPA fallback'inin index.html döndürmesi

    Adım 3 — Middleware davranışını test et

    run_worker_first kapalıyken Worker’ın statik dosya isteklerini görmediğini kanıtla.

    wrangler tail — statik dosya istekleri sırasında hiç log düşmediği, yalnızca /api/ isteklerinin göründüğü
    Aynı test run_worker_first açıkken — bu kez statik istekler de log'da görünüyor

    Adım 4 — Yayına al ve önbellek başlıklarını incele

    npx wrangler deploy
    curl -sI https://<worker>/ | grep -iE 'cache-control|etag|cf-cache-status'
    curl -I çıktısı — Cache-Control, ETag ve CF-Cache-Status (HIT/MISS) başlıkları
    Metrics ekranı — Asset requests ve Invocations sayaçlarının yan yana farkı; statik isteklerin çağrı sayılmadığı

    Ölçüm

    ÖlçütDeğer
    Statik istek sayısı—
    Worker çağrısı sayısı—
    Statik isteklerin faturaya etkisi—
    run_worker_first açıkken çağrı artışı—
    İlk bayta kadar süre (statik / API)—

    Fiyatlandırma

    Aşağıdaki rakamlar 31 Ağustos 2026 tarihinde Cloudflare’in resmî fiyatlandırma sayfasından doğrulanmıştır.

    ÜcretsizÜcretli (Standard)
    Aylık taban ücret0 USD5 USD
    İstekGünde 100.000Ayda 10 milyon dahil, sonrası milyon başına 0,30 USD
    CPU süresiÇağrı başına en fazla 10 msAyda 30 milyon CPU ms dahil, sonrası milyon CPU ms başına 0,02 USD
    Bekleme süresiÜcretlendirilmezÜcretlendirilmez, sınır yok
    Çağrı başına CPU üst sınırı10 msVarsayılan 30 sn, en fazla 5 dk
    Betik boyutu (sıkıştırılmış)3 MB10 MB
    Bellek (izolat başına)128 MB128 MB
    İstek başına alt istek5010.000
    Hesap başına Worker100500
    Hesap başına Cron tetikleyici5250
    Statik dosya istekleriÜcretsiz ve sınırsızÜcretsiz ve sınırsız

    İki gerçek hesap

    Örnek A — orta ölçekli haber sitesi

    • Günde 300.000 istek → ayda 9 milyon istek (10 milyon dahil sınırının altında)
    • İstek başına ortalama 3 ms CPU → ayda 27 milyon CPU ms (30 milyon dahil sınırının altında)
    • Aylık toplam: 5 USD

    Örnek B — yoğun bir API ağ geçidi

    • Günde 2 milyon istek → ayda 60 milyon istek
    • İstek başına ortalama 5 ms CPU → ayda 300 milyon CPU ms
    KalemHesapTutar
    Taban—5,00 USD
    Fazla istek(60 − 10) milyon × 0,30 USD15,00 USD
    Fazla CPU(300 − 30) milyon ms × 0,02 USD5,40 USD
    Toplam25,40 USD / ay

    Aynı yükü kendi sunucunda karşılamak, yedekli iki sanal makine, bir yük dengeleyici ve bunların bakımı demektir. Karşılaştırmayı yaparken sadece sunucu kirasını değil, işletim maliyetini de hesaba kat.

    Lisanslama ve hukuki çerçeve

    Bu bölüm, kurumsal satın alma ve hukuk birimlerinin en çok sorduğu noktaları toplar.

    Hizmetin lisansı. Cloudflare Workers tescilli bir hizmettir. Kullanımı Cloudflare Hizmet Şartları ve Kabul Edilebilir Kullanım Politikası ile düzenlenir. Kaynak kodu size lisanslanmaz; hizmete abone olursunuz.

    Çalışma zamanı açık kaynaktır. Workers’ı çalıştıran motor olan workerd, Apache 2.0 lisansıyla GitHub’da yayımlanır. Bu pratikte şu anlama gelir: kodunu yerelde wrangler dev ile üretimdekiyle aynı çalışma zamanında test edebilirsin ve teorik olarak workerd’i kendi altyapında çalıştırabilirsin. Ancak KV, R2, D1 gibi bağlı servisler ve global ağın kendisi açık kaynak değildir — tam bir kendi kendine barındırma seçeneği yoktur.

    Yazdığın kod sana aittir. Worker’ına yüklediğin kodun mülkiyeti sende kalır. Cloudflare, hizmeti sunmak için gerekli olan işleme hakkını alır.

    Veri işleme ve KVKK. Kurumsal kullanımda ilgili belgeler Cloudflare’in Veri İşleme Ek Sözleşmesi ve alt işleyici listesidir. Trafiğin hangi coğrafyada işleneceğini kısıtlamak isteyen kurumlar için Data Localization Suite adında ayrı ücretlendirilen bir eklenti bulunur; standart Workers kullanımında kod, isteği karşılayan herhangi bir şehirde çalışabilir.

    Sık yapılan hatalar

    await edilmeyen sözler (floating promise). Yanıtı döndürdükten sonra biten bir iş varsa mutlaka ctx.waitUntil() içine alın. Aksi halde Worker yanıtı gönderdiği anda kapanır ve iş yarım kalır.

    Genel (global) kapsamda durum tutmak. İzolatlar isteğe göre oluşturulup yok edilir ve farklı kullanıcılar aynı izolatı paylaşabilir. Genel değişkende kullanıcıya özel veri tutmak, veriyi başka kullanıcıya sızdırır. Kullanıcı durumu için Durable Objects veya KV kullanın.

    Sırları kodun içine yazmak. API anahtarları wrangler secret put ile saklanır, wrangler.jsonc içindeki vars alanına değil. vars içeriği yayına alınan pakette açık metin olarak durur.

    Gövdeyi iki kez okumak. request.body tek kullanımlık bir akıştır. Hem imza doğrulaması hem de iletme için gerekiyorsa request.clone() kullanın.

    Yanıtı tamamen belleğe almak. Büyük yanıtları await response.text() ile toplamak yerine akış olarak geçirin; 128 MB bellek sınırına takılmadan ve ilk baytı geciktirmeden çalışır.

    Sıkça sorulan sorular

    Workers ile AWS Lambda arasındaki fark nedir?
    Üç temel fark var. Soğuk başlangıç: Lambda her çağrı için mikro sanal makine ayağa kaldırır (yüzlerce ms); Workers V8 izolatı kullanır, kurulum milisaniyenin altındadır. Konum: Lambda seçtiğin tek bir bölgede çalışır; Workers isteği karşılayan şehirde çalışır. Faturalama: Lambda bekleme süresini de ücretlendirir; Workers yalnızca CPU süresini ücretlendirir. Buna karşılık Lambda daha fazla bellek, daha uzun çalışma süresi ve native ikili çalıştırma imkânı sunar.
    Ücretsiz plan gerçekten üretimde kullanılabilir mi?
    Günde 100.000 istek ve çağrı başına 10 ms CPU sınırıyla, düşük trafikli siteler ve iç araçlar için evet. Ancak çağrı başına 10 ms CPU sınırı gerçek bir kısıttır: JSON şablonlama veya imza doğrulama gibi işlerde bu sınıra takılabilirsin. Ayda 5 USD ödeyerek geçtiğin ücretli plan bu sınırı 30 saniyeye çıkarır — üretim kullanımı için ilk yükseltme kalemi budur.
    Alan adımı Cloudflare’e taşımadan Workers kullanabilir miyim?
    Kısmen. Her Worker otomatik olarak bir *.workers.dev adresi alır ve bunu hemen kullanabilirsin. Kendi alan adında çalıştırmak için ise alan adının DNS’inin Cloudflare üzerinde olması gerekir — yani ad sunucularını Cloudflare’e yöneltmen gerekir. Alan adını taşımana (registrar değiştirmene) gerek yoktur, yalnızca DNS yönetimini devretmen yeterlidir.
    Mevcut Node.js kütüphanelerimi kullanabilir miyim?
    Saf JavaScript olanların çoğu çalışır. fs, net gibi Node çekirdek modüllerine dayananlar için nodejs_compat uyumluluk bayrağını açman gerekir ve bu bayrak da her modülü karşılamaz. Native derlenmiş eklenti (.node dosyası) içeren paketler çalışmaz; bunlar için Containers kullanılır.
    Türkiye’de Cloudflare noktası var mı? Gecikme ne kadar olur?
    Evet, hatta iki tane: Cloudflare’in ağ sayfasındaki şehir listesinde Türkiye’den İstanbul ve İzmir geçiyor. İstanbul 2018’de açıldı ve o tarihte şirketin 124. veri merkeziydi. Ancak Türkiye’den gelen trafiğin oraya düşeceğinin garantisi yok: belirleyici olan operatörün peering yapılandırmasıdır. Bu portalı hazırlarken iki ayrı Türk hattından ölçtük, biri Frankfurt’a biri Milano’ya düştü. Kendi hattını ölç: curl -s https://alanadin/cdn-cgi/trace | grep colo
    Worker’ım şirket içindeki veritabanıma nasıl bağlanır?
    Worker ham TCP açabilir — cloudflare:sockets modülündeki connect() API’si bunun içindir; Hyperdrive de zaten bunun üzerine kuruludur. Ama ham soket doğru çözüm değildir: stateless bir isolate her çağrıda TCP el sıkışması, TLS ve kimlik doğrulama için yedi tur atmak zorunda kalır. Pratik yollar şunlar: (1) Hyperdrive — bağlantı havuzunu veritabanına yakın tutar, kurulum turlarını kenarda tamamlar. (2) Cloudflare Tunnel — iç ağdaki HTTP API’yi güvenlik duvarında port açmadan yayınlar. (3) Workers VPC — bulut sağlayıcının private network’üne bağlanır. Şirket içi PostgreSQL için Hyperdrive + Tunnel birlikte kullanılır.
    Workers Static Assets nedir, Pages'ten farkı ne?
    Workers artık statik dosyaları da sunabiliyor — assets.directory ile bir klasörü işaret edersin, Cloudflare hem Worker kodunu hem dosyaları tek deploy'da yayına alır. Kritik davranış farkı: Workers'ta önce statik dosya aranır, bulunamazsa Worker çalışır. Pages'te sıra terstir (önce Function). Auth kontrolü gibi her isteği görmesi gereken bir katmanın varsa run_worker_first ayarını açman gerekir.
    Statik dosya istekleri için ücret ödüyor muyum?
    Hayır. Resmî ifade: “Requests to static assets are free and unlimited.” Depolama için de ek ücret yok: “There is no additional cost for storing Assets.” Tek istisna: Workers Caching açıksa cache'ten servis edilen istekler normal istek ücretiyle faturalanır — bu, aynı fiyat sayfasındaki iki dipnotun birbirini nitelemesinden doğan bir incelik.
    Cron Trigger ile zamanlanmış iş çalıştırabilir miyim?
    Evet. triggers.crons ile UTC bazlı zamanlama tanımlar, scheduled handler'ı yazarsın. Sınırlar: hesap başına ücretsizde 5, ücretlide 250 tetikleyici. Duvar saati sınırı 15 dakika. Değişikliklerin yayılması 15 dakikayı bulabilir. 15 dakikayı aşan işler için Workflows'a bak.
    Kaç Worker deploy edebilirim?
    Hesap başına ücretsizde 100, ücretlide 500. Ayrıca rollback derinliği sınırlıdır: “You can only roll back to the 100 most recently published versions.” Rollback binding'leri geri almaz — resmî ifade: “Resources connected to your Worker will not be changed during a rollback.”
    Beklenmedik bir fatura ile karşılaşabilir miyim?
    Workers’ta kullanımı otomatik durduran sabit bir harcama tavanı yoktur; trafik artarsa fatura da artar. Korunmak için üç şey yapabilirsin: Cloudflare panelinden fatura bildirimi kurmak, Worker’ın önüne hız sınırlama (Rate Limiting) koymak ve kodun içinde günlük istek sayacını KV üzerinde tutup eşik aşılınca erken yanıt dönmek. Ücretsiz planda ise sınır aşıldığında Worker çalışmayı durdurur, fatura oluşmaz.
    Python desteği üretimde kullanılabilir durumda mı?
    Python Workers mevcuttur ancak JavaScript/TypeScript ile aynı olgunlukta değildir. Paket desteği sınırlıdır ve soğuk başlangıç davranışı farklıdır. Yeni bir proje başlatıyorsan ve özel bir Python gereksinimin yoksa TypeScript ile başlamak daha az sürprizle sonuçlanır.

    İlgili servisler

    • Durable ObjectsHer nesnenin tek bir instance’ı olan, state tutan compute; sohbet odası, oyun oturumu, sayaç gibi işler için.
    • Workers KVOkuma ağırlıklı, global olarak replike edilen key-value store.
    • R2S3 uyumlu object storage — çıkış (egress) trafiği ücretsiz.
    • D1Workers’a bağlanan serverless SQLite veritabanı.
    • Cloudflare TunnelŞirket içi sunucunu, güvenlik duvarında port açmadan Cloudflare’e giden bir tünelle yayınlar.
    • PagesGit deposuna push ettiğinde frontend sitesini build edip global olarak yayına alır.

    Bu sayfadaki fiyat ve özellik bilgileri 31 Ağustos 2026 tarihinde Cloudflare’in resmî kaynaklarından doğrulanmıştır. Cloudflare fiyatlandırmasını önceden haber vermeden değiştirebilir; bağlayıcı bilgi içinresmî sayfaya bakın.

    Hata bildir

    Yanlış bir rakam, eskimiş bir bilgi veya bozuk bir bağlantı mı buldun? Bildir, kaynağıyla birlikte kontrol edelim.