İçeriğe geç
← STÜDYO NOTLARI

Web mühendisliği

Next.js Cloudflare Workers’ta nasıl çalışır? vinext ve kenar önbelleği

amgsoftwarestudio.com’un Next.js 16 kodu vinext ile Worker’a derleniyor; herkese açık HTML, yayın sürümüne bağlı 5 dakikalık bir kenar önbelleğinden geliyor.

KISA CEVAP

amgsoftwarestudio.com, Next.js 16 App Router kodunu vinext ile derleyip tek bir Cloudflare Worker olarak çalıştırıyor. Worker, anonim GET isteklerindeki herkese açık HTML’i Cache API’de 300 saniye tutuyor ve önbellek anahtarına Cloudflare’in yayın sürümü kimliğini ekliyor. Bu sayede her yeni yayın kendi anahtarıyla başlıyor, temizleme gerekmiyor. 31 Ekim 2026 ölçümünde önbellekten dönen sayfaların ilk bayt süresi 0,15–0,25 saniyeydi.

Derleme adımları hangi sırayla çalışıyor?

Stüdyonun kendi sitesi amgsoftwarestudio.com, Next.js 16 App Router ve next-intl ile yazılmış iki dilli bir uygulama. Türkçe kökte, İngilizce /en altında yaşıyor. Üretim derlemesi pnpm build:vinext komutuyla başlıyor ve önce prebuild:vinext çalışıyor: git geçmişinden sitemap tarihlerini üreten build-lastmod.mjs, paylaşım görsellerini üreten seo:assets ve duyarlı görselleri hazırlayan images:build. Ardından vinext build uygulamayı Vite üzerinden derliyor.

vite.config.ts iki eklentiden oluşuyor: vinext({ cache: { cdn: cdnAdapter() } }) ve @cloudflare/vite-plugin. Cloudflare eklentisi rsc ortamını ana ortam, ssr ortamını onun alt ortamı olarak tanımlıyor; yani React Server Components ve sunucu tarafı HTML üretimi aynı Worker paketine giriyor. Derleme çıktısı dist/server/wrangler.json ve dist/client statik dosyaları. Worker, nodejs_compat bayrağıyla çalışıyor; ortam bağlamaları AsyncLocalStorage ile istek boyunca taşınıyor.

Yayın bilinçli olarak elle yapılıyor. 7 Eylül 2026’da alınan kararla GitHub’a push siteyi yayınlamıyor. GitHub Actions iş akışı yalnızca kontrol ediyor:

  • kilitli bağımlılıklarla kurulum
  • pnpm typecheck ve pnpm lint
  • iletişim formu testleri
  • pnpm build:vinext ile üretim Worker derlemesi

Yayın, yerelde wrangler dev ile gözden geçirilen derlemenin wrangler deploy --config dist/server/wrangler.json ile gönderilmesiyle oluyor. Bu yüzden CI tarafında hiçbir Cloudflare dağıtım sırrı yok.

Worker bir isteği uygulamaya vermeden önce ne yapıyor?

worker.ts ince bir ön katman. vinext’in fetch-handler’ını sarıyor ve isteği uygulamaya vermeden önce sırayla şu kararları veriyor:

  • Düz HTTP ile gelen apex istekleri, www.amgsoftwarestudio.com ve *.workers.dev adresleri HTTPS apex’e 308 ile yönleniyor.
  • /tr ve /tr/... adresleri kalıcı takma ad sayılıyor ve köke 308 ile yönleniyor, çünkü Türkçe varsayılan dil.
  • Özel analitik paneli ve /api/events uygulamaya hiç girmeden Worker içinde cevaplanıyor.
  • Geri kalan her şey önbellek kontrolünden geçip vinext’e gidiyor.

HTTP’yi tespit etmek için x-forwarded-proto ve cf-visitor başlıklarına bakılıyor. Yerel wrangler dev bu başlıkları göndermediği için geliştirme ortamı yönlendirme döngüsüne girmiyor; koddaki yorum da tam bunu söylüyor.

HSTS iki katmanda tanımlı. next.config.ts bütün sayfalara max-age=63072000; includeSubDomains; preload ekliyor. Worker ise yanıtta bu başlık yoksa max-age=31536000 ekliyor; bu çoğunlukla uygulamaya hiç ulaşmayan yönlendirmeler için geçerli. 31 Ekim 2026’da ölçtüğümüzde /en iki yıllık başlığı, www yönlendirmesi bir yıllık başlığı döndürdü. http://amgsoftwarestudio.com/en isteği HTTP/1.1 308 ile https://amgsoftwarestudio.com/en adresine, /tr/about ise /about adresine gitti.

HTML önbelleği neden yayın sürümü kimliğiyle anahtarlanıyor?

wrangler.jsonc içinde version_metadata bağlaması CF_VERSION_METADATA adıyla tanımlı. Worker her istekte bu bağlamanın id alanını okuyor ve önbellek anahtarını, sayfa adresine __studio_version=<id> sorgu parametresi ekleyerek kuruyor. Yeni bir wrangler deploy yeni bir sürüm kimliği demek; dolayısıyla eski HTML’e kimse ulaşamıyor ve yayından sonra önbellek temizleme adımı yok. Eski kayıtlar süreleri dolunca kendiliğinden düşüyor.

Bir isteğin önbelleğe girmesi için bütün koşulların aynı anda sağlanması gerekiyor:

  • Metot GET ve yol, herkese açık sayfaları tanımlayan düzenli ifadeyle eşleşiyor: kök, /about, /process, /services, /work, /apps, /games, /web, /insights, /reviews ve bunların tek seviyeli alt sayfaları, /en önekli ya da öneksiz.
  • Sorgu dizesi yok.
  • rsc, next-router-state-tree ve authorization başlıkları yok.
  • accept başlığı text/html içeriyor.

İletişim sayfası bu listede değil; form ve sorgu varyantları her zaman dinamik kalıyor. Önbelleğe yalnızca 200 durumlu ve text/html içerik tipli yanıtlar yazılıyor. Yazmadan önce set-cookie siliniyor, çünkü dil açık URL ile belirleniyor ve herkese açık HTML’in tercih çerezine ihtiyacı yok. Kenarda saklanan kopya Cache-Control: public, max-age=300 taşıyor, tarayıcıya giden kopya ise no-store. Yazma işlemi ctx.waitUntil ile yanıtı bekletmeden yapılıyor ve her yanıt X-Studio-Cache: HIT ya da MISS başlığıyla işaretleniyor.

Canlı sitede önbellek ne kadar fark ediyor?

31 Ekim 2026’da canlı siteye curl ile yalnızca GET istekleri gönderdik. Yanıtlar Sofya (SOF) Cloudflare veri merkezinden geldi. Bunlar tek bir konumdan alınmış küçük örnekler; genel bir performans iddiası değil.

  • /en/services: iki MISS isteğinde ilk bayt 0,46 ve 0,54 saniye, ardından gelen HIT isteğinde 0,15 saniye.
  • /en ve kök sayfa: art arda üçer HIT, ilk bayt 0,16–0,25 saniye.
  • /en/about: ilk istek MISS, sonraki üç istek HIT.
  • /en HTML’i sıkıştırmasız 559.887 bayt, Brotli ile yaklaşık 71 KB.

HTML’in bu kadar büyük olmasının sebebi experimental.inlineCss. CSS ayrı bir dosya yerine HTML’in içinde geliyor; bu, ilk çizimi engelleyen stil isteğini kaldırıyor ama CSS’in ayrı önbelleklenmesinden vazgeçmek demek. Kenar önbelleği bu büyük belgeyi yeniden üretmemeyi sağlıyor.

Önbellek dışı kalması gereken yollar da beklendiği gibi davrandı. /en/contact ve /en?x=1 yanıtları X-Studio-Cache başlığı taşımadı ve no-store, must-revalidate döndü. RSC: 1 başlıklı istek 307 aldı. Ölçümün başında /en için art arda iki MISS de gördük; birkaç dakika sonraki üç istek HIT döndü. Önbellek her veri merkezinde ayrı dolduğu için ilk ziyaretlerde MISS beklenmeli; doğrulamayı tek istekle değil, birkaç tekrarla yapmak gerekiyor.

Analitik uç noktası kötüye kullanıma karşı nasıl sınırlanıyor?

Site, üçüncü taraf bir izleme betiği yerine kendi olay uç noktasını kullanıyor. /api/events isteği Worker içinde şu sırayla eleniyor:

  • POST dışındaki metotlar 405 alıyor; denedik, GET isteği gerçekten 405 döndü.
  • origin başlığı sitenin kendi kökeni değilse 403.
  • Gövde 2.048 baytı aşarsa 413; hem content-length başlığı hem okunan gövde ayrı ayrı kontrol ediliyor.
  • Olay adı 17 elemanlı clientEvents listesinde yoksa ya da yol cleanPath düzenli ifadesine uymuyorsa 400.
  • DNT: 1 gönderen tarayıcılar, kullanıcı ajanında bot, crawler, spider veya headless geçen istekler ve oturum açmış yönetici ziyaretleri 204 alıyor ama kaydedilmiyor.
  • Oran sınırlayıcı ANALYTICS_LIMITER geçilmezse 429.

Sınır wrangler.jsonc içinde 60 saniyede 60 istek olarak tanımlı ve anahtar event: ile istemci IP’sinden oluşuyor. İletişim formu ve yönetici girişi ayrı sınırlayıcılara sahip: ikisi de 60 saniyede 5 istek. Sınırlayıcı bağlaması yoksa kod 429 döndürüyor, yani kapalı tarafta başarısız oluyor.

Ülke bilgisi istemci gövdesinden değil Cloudflare’in request.cf.country değerinden alınıyor. Yönlendiren adres yalnızca ana makine adı olarak, UTM etiketleri 64 karakterle sınırlı küçük harf desenle tutuluyor. D1 veritabanına yalnızca gün, olay, yol, kaynak ve cihaz bazlı toplam sayaçlar yazılıyor; ziyaretçi kimliği, ham IP, form metni veya sorgu dizesi saklanmıyor.

Görseller ücretli dönüştürme servisi olmadan nasıl duyarlı oluyor?

Workers üzerinde Next.js’in varsayılan görsel optimizasyonuna güvenmek yerine görseller derleme anında hazırlanıyor. scripts/build-responsive-images.mjs, public/media altındaki her WebP, PNG ve JPEG dosyasını sharp ile yedi genişliğe çeviriyor: 160, 320, 480, 640, 960, 1280 ve 1920 piksel. Çıktılar WebP, kalite 80, effort: 4. Hiçbir görsel kaynağından büyük üretilmiyor (withoutEnlargement). Bellek kullanımı öngörülebilir kalsın diye işler 4 eşzamanlı işçiden oluşan bir havuzla yürüyor.

Repoda 269 kaynak görsel var; 7 boyutla 1.883 dosya ve yaklaşık 51 MB çıktı oluşuyor. next.config.ts içinde loader: custom ile tanımlanan lib/image-loader.ts, istenen genişliğe eşit ya da ondan büyük ilk hazır boyutu seçip /media/responsive/.../<genişlik>.webp yolunu döndürüyor. StudioImage bileşeni bu yedi boyutla doğrudan srcSet yazıyor ve öncelikli görseller için react-dom preload çağırıyor. Böylece aynı işaretleme hem Next.js geliştirme sunucusunda hem Worker çalışma ortamında birebir aynı.

Paylaşım görselleri de aynı yaklaşımla üretiliyor. scripts/build-seo-assets.ts, 1200×630 boyutunda bir SVG şablonu kuruyor, başlığı en fazla dört satıra bölüyor ve ürün ekranlarını üstüne bindirip PNG’ye çeviriyor. Her dil için bir stüdyo görseli, 56 proje ve 8 hizmet: 2 × 65 = 130 görsel, yaklaşık 17 MB. Teslim notunda da açıkça yazdığı gibi ücretli Cloudflare Images bağımlılığı yok.

llms.txt, sitemap tarihleri ve robots.txt nasıl üretiliyor?

app/llms.txt/route.ts, force-static olarak işaretlenmiş bir route handler. Hizmetleri, stüdyo sayfalarını, mühendislik notlarını ve indekslenen projeleri, sayfalardaki ve Organization şemasındaki aynı içerik kaynaklarından düz metin olarak listeliyor; yapay zekâ botlarına ayrı içerik sunulmuyor. Yanıt Cache-Control: public, max-age=3600 ile dönüyor. 31 Ekim 2026’da dosya 13.119 bayt, 79 satır ve 66 bağlantı satırıydı.

Sitemap’in <lastmod> değerleri derleme anında git log -1 --format=%cI ile üretiliyor. build-lastmod.mjs içerik dosyalarını yedi gruba ayırıyor: oyun, uygulama, web, notlar, hizmetler, yasal metinler ve stüdyo. Her grubun tarihi, o gruptaki dosyalara dokunan son commit’in tarihi. Git okunamazsa önceki JSON değeri, o da yoksa o anki zaman kullanılıyor. Script yorumundaki gerekçe şu: her derlemede “şimdi” yazmak arama motorlarına yanlış sinyal veriyor.

Bu yaklaşımın bir bedeli var. Proje grupları public/media/projects klasörünü de içerdiği için tek bir proje görseli değişince o türdeki bütün projelerin tarihi güncelleniyor. 31 Ekim 2026’da canlı sitemap’teki 154 adresin 132’si aynı güne ait tarih taşıyordu; notlar 8 Eylül, yasal sayfalar 2 Ekim tarihindeydi.

robots.ts arama, yapay zekâ arama ve eğitim botları dahil 17 tarayıcıyı adıyla listeliyor ve hepsine izin veriyor. Adı geçen bir grup * grubunun yerine geçtiği için özel yollar her grupta tekrar ediliyor.

Lighthouse ölçümleri ne gösterdi?

Repoda saklanan docs/seo-geo-evidence/lighthouse-2026-09-07.json, Lighthouse 13.4.1 ile /en sayfasında mobil simülasyonla alınmış bir rapor. Sonuçlar: performans 76, erişilebilirlik 100, iyi uygulamalar 100, SEO 100. FCP 3,2 s, LCP 4,4 s, toplam engelleme süresi 10 ms, CLS 0, sunucu yanıtı yaklaşık 60 ms, toplam aktarım 967 KiB. Teslim notu bu sonucu “kusursuz performans” olarak sunmuyor; yavaş mobil bağlantıda FCP ve LCP’nin hâlâ iyileştirme alanı olduğunu yazıyor.

Sonraki büyüme çalışmasının notları sıralı ölçümleri kaydediyor. Taze mobil temel ölçüm performans 61 ve LCP 8,7 s verdi; LCP öğesi otomatik oynayan vitrin videosuydu. Mobilde video yerine 480, 768 ve 1080 piksellik bir poster gösterilmeye başlandı, video yalnızca oynat düğmesiyle yükleniyor. Ana sayfa kartlarına yalnızca gösterilen alanları gönderen sıkıştırılmış veri ilk yüklemeyi 1.138.831 bayttan yaklaşık 689.000 bayta, HTML’i 69.636 bayttan 36.521 bayta indirdi; yine de iki soğuk ölçümde performans 59’da kaldı, çünkü baskın gecikme engelleyici stil dosyasıydı.

Son adım inlineCss oldu. Belgelenen son canlı mobil ölçüm: performans 93, FCP 1,7 s, LCP 2,9 s, TBT 100 ms, CLS 0, aktarım 713.052 bayt. Not açıkça 2,9 saniyenin hâlâ 2,5 saniyelik “iyi” eşiğin üstünde olduğunu ve bunların saha verisi değil laboratuvar sonucu olduğunu söylüyor. Bu son raporun ham dosyası repoda değil.

Benzer bir site kuracaklar için kontrol listesi

Next.js uygulamasını Cloudflare Workers’a taşırken amgsoftwarestudio.com’da işe yarayan kararlar şunlar:

  • Yönlendirmeleri ve küçük API uçlarını uygulamadan önce, Worker giriş dosyasında çözün. 308 kullanın; dil takma adlarını da kalıcı yönlendirme sayın.
  • HTML önbelleğinin anahtarına yayın sürümü kimliğini ekleyin. Temizleme adımı ortadan kalkar, eski yayın kendiliğinden düşer.
  • Neyin önbelleğe gireceğini açık bir izin listesiyle tanımlayın: yalnızca GET, sorgusuz, RSC ve yetki başlıkları olmayan, 200 dönen HTML.
  • Önbelleğe yazmadan önce set-cookie başlığını silin ve tarayıcıya no-store gönderin; kenar ve tarayıcı süreleri farklı olabilir.
  • Her yanıta HIT ya da MISS başlığı koyun; canlı doğrulamayı tek bir curl ile yapabilirsiniz.
  • Analitik uç noktasında köken, boyut, olay listesi ve IP bazlı oran sınırı kullanın; sınırlayıcı yoksa kapalı tarafta başarısız olun.
  • Görselleri derleme anında sabit genişliklere üretin ve özel bir yükleyiciyle eşleyin.
  • Sitemap tarihlerini git geçmişinden alın, ama grupları dar tutun.
  • CI’ı doğrulama için kullanın, yayını gözden geçirilmiş bir derlemeyle yapın.
  • Lighthouse sonuçlarını tarih, sayfa ve koşullarla birlikte saklayın; kötü sonuçları da.

AMG Software Studio, Cloudflare ve Next.js üzerinde bu tür hızlı, çok dilli kurumsal siteler ve web ürünleri geliştiriyor.

PROJENİZE UYARLAYALIM

Web Geliştirme

Next.js ile kurumsal site, ürün sitesi ve SaaS paneli. Hız ve arama görünürlüğü baştan planlanır.