CDN Önbelleği Nasıl Temizlenir? Güncelleme Sonrası Eski İçerik Sorunu
Yazılım

CDN Önbelleği Nasıl Temizlenir? Güncelleme Sonrası Eski İçerik Sorunu

Dağıtım başarılı, sunucu doğru içeriği veriyor, ama sayfaların yarısı hâlâ eski. Sebebi Next'in bir yıllık önbellek varsayılanı ve dağıtımdan haberi olmayan bir CDN.

AA
Algoritma Ajans
Yayın ekibi
9 dk okuma

Dağıtım yaptınız ama sayfaların bir kısmı hâlâ eski görünüyorsa sebep neredeyse her zaman CDN önbelleğidir. Next.js statik sayfalara bir yıllık s-maxage basar; Vercel dışındaki hiçbir CDN dağıtımınızdan haberdar olmadığı için bu süreyi harfiyen uygular. Çözüm önbellek süresini uygulamadan makul bir değere çekmektir.

Kendi sitemizi yeni sürüme geçirdik. Build temiz, dağıtım başarılı, sunucu doğru içeriği veriyor. Sonra kontrol ettik: 108 sayfanın 50'si hâlâ eski içeriği sunuyordu. Analytics etiketi yok, eski başlıklar duruyor, güncellediğimiz meta açıklamalar görünmüyor.

Sunucuda hata yoktu. Sorun daha sinsi bir yerdeydi ve büyük ihtimalle sizin sitenizde de var.

Belirti

Dağıtımdan sonra site açılıyor, hata vermiyor, ama bazı sayfalar eski. Hepsi değil — bir kısmı. Yeniden yüklerseniz bazen düzeliyor, bazen düzelmiyor. Farklı cihazdan bakınca farklı sonuç alıyorsunuz.

Bu dağılım tesadüfi değil: CDN'in her düğümü ayrı önbellek tutar. Sizin isteğiniz İstanbul düğümüne düşerse taze, Frankfurt düğümüne düşerse eski içerik alırsınız.

Teşhisi zorlaştıran ikinci katman tarayıcı önbelleği. Sayfa tarayıcınızda da saklanmış olabilir; Ctrl+Shift+R ile sert yenileme yaptığınızda düzeliyorsa sorun tarayıcıdadır, düzelmiyorsa CDN'dedir. Bu ayrımı yapmadan saatlerce yanlış yerde arama yapabilirsiniz.

Kaldırımda yan yana duran üç gazete kutusu; hepsinde aynı gazete ama farklı tarihli sayılar — ziyaretçinin hangi CDN düğümüne düştüğüne göre farklı içerik görmesi

Sebep

Next.js, statik olarak ön-render edilen sayfalara şu başlığı basar:

cache-control: s-maxage=31536000

31.536.000 saniye — bir yıl. s-maxage, paylaşımlı önbellekler (yani CDN'ler) içindir. Next bunu güvenle yapabilir çünkü Vercel'de her dağıtım CDN önbelleğini otomatik temizler. Sayfa bir yıl önbellekte durabilir, çünkü yeni sürüm çıktığı anda o önbellek zaten siliniyor.

Vercel dışına çıktığınızda bu varsayım çöker.

Hostinger, Cloudflare, kendi kurduğunuz nginx önbelleği — hiçbiri dağıtımınızdan haberdar değildir. s-maxage=31536000 gördüklerinde tam olarak dediğinizi yaparlar: sayfayı bir yıl saklarlar.

Bizim ölçtüğümüz değer şuydu:

cache-control: s-maxage=31536000
age: 20031
x-hcdn-cache-status: HIT

age: 20031 — o kopya 5,5 saattir önbellekte duruyordu ve doğal olarak tazelenmesi için 364 gün daha vardı.

Önbellek başlıklarının sözlüğü

Bu başlıkların anlamını bilmeden doğru değeri seçemezsiniz. Kısa bir sözlük:

Direktif Kimi ilgilendirir Ne yapar
max-age=N Tarayıcı ve CDN N saniye taze say
s-maxage=N Yalnızca CDN N saniye taze say, max-age'i ezer
stale-while-revalidate=N CDN Süre dolduktan sonra N saniye eski kopyayı sun, arkada tazele
no-store Hepsi Hiç saklama
no-cache Hepsi Sakla ama kullanmadan önce sor
private Tarayıcı Yalnızca son kullanıcı saklayabilir, CDN saklayamaz
must-revalidate Hepsi Süre dolunca eski kopyayı sunma
immutable Tarayıcı Süre boyunca yeniden sorma

En sık karıştırılan ikisi no-cache ve no-store. no-cache "saklama" demek değildir — "sakla ama her seferinde bana sor" demektir. Gerçekten saklanmasını istemiyorsanız no-store yazmanız gerekir. Oturum içeren sayfalarda bu fark bir güvenlik meselesine dönüşür.

Neden SEO açısından ciddi

Bu sadece bir görüntü sorunu değil.

Google eski sayfayı tarar. Googlebot da CDN'den geçer. Meta açıklamanızı düzelttiyseniz, başlığınızı değiştirdiyseniz, yapısal veri eklediyseniz — Google bunların hiçbirini görmez. Değişikliğin etkisini ölçmeye çalışırken aslında hiç yayına girmemiş bir değişikliği ölçersiniz.

Ölçümleme eksik toplar. Analytics etiketini yeni eklediyseniz, eski kopyadan gelen ziyaretler hiç sayılmaz. Trafiğiniz olduğundan düşük görünür ve bunu fark etmezsiniz.

Hata düzeltmeleri yayılmaz. Kırık bir bağlantıyı düzelttiniz; ziyaretçilerin bir kısmı hâlâ kırık sayfayı görüyor.

Yapay zeka botları da eski kopyayı okur. GPTBot ya da PerplexityBot sitenize uğradığında CDN'in verdiği sürümü alır. Yeni eklediğiniz llms.txt ya da düzelttiğiniz şema, önbellek dolana kadar onlar için de yok hükmündedir.

Bir de sitemap tarafı var: lastmod değerinizi doğru tutuyor olsanız bile, CDN eski sayfayı sunuyorsa Google "değişti dedin ama değişmemiş" sonucuna varır ve lastmod sinyaline olan güvenini kaybeder. İki sorun birbirini besliyor.

Üç ayrı önbellek katmanı var, karıştırmayın

"Sayfa eski geliyor" şikâyetinin arkasında üç farklı katman olabilir ve çözümleri farklıdır.

1. Tarayıcı önbelleği. Ziyaretçinin makinesinde. max-age yönetir. Sert yenileme (Ctrl+Shift+R) ile aşılır — yani bir ziyaretçiye "sayfayı yenile" diyerek çözülüyorsa katman budur.

2. CDN / paylaşımlı önbellek. Sizinle ziyaretçi arasında. s-maxage yönetir. Sert yenileme işe yaramaz; farklı bir sorgu dizesiyle ya da önbellek temizliğiyle aşılır.

3. Uygulamanın kendi önbelleği. Next.js'in sayfa ve veri önbelleği. Sunucu tarafında, CDN'e hiç ulaşmadan önce devrede. Yeniden başlatma ya da yeniden doğrulama (revalidation) ile aşılır.

Üçü aynı anda devrede olabilir ve bir tanesini çözmek diğerlerini çözmez. Teşhis sırası şu olmalı:

# Sunucu ne diyor? (CDN'i baypas et)
curl -s "https://siteniz.com/sayfa?x=$RANDOM" | grep -c "yeni-metin"

# CDN ne diyor?
curl -s "https://siteniz.com/sayfa" | grep -c "yeni-metin"

Birinci komut 1, ikinci 0 dönüyorsa sorun CDN'de. İkisi de 0 dönüyorsa sorun uygulamanın kendi önbelleğinde ya da dağıtım hiç yayına girmemiş.

Üçüncü katman için Next.js'in kendi araçları var: sayfa bazında revalidate süresi tanımlamak ya da bir içerik güncellendiğinde ilgili yolu programatik olarak yeniden doğrulamak. Bu API'lerin bu sürümdeki tam kullanımı için node_modules/next/dist/docs/ altındaki önbellek rehberine bakın — sürümler arasında belirgin biçimde değişti ve ezberden yazmak hataya davetiye çıkarıyor.

İdeal kurgu şudur: içerik değiştiğinde uygulama kendi önbelleğini yeniden doğrular, dağıtım betiği CDN'i temizler, s-maxage de bunların hiçbiri çalışmazsa en fazla bir saatlik gecikme garantisi verir. Üç katmanlı bir savunma.

Çözüm

CDN'i her dağıtımda temizleyebiliyorsanız temizleyin. Çoğu paylaşımlı hosting'de böyle bir düğme yok ya da güvenilir çalışmıyor. Kalıcı çözüm önbellek süresini makul bir değere çekmek.

next.config.ts içinde:

async headers() {
  return [
    {
      // HTML sayfaları: CDN bir saat önbeklesin, sonra arka planda tazelesin.
      source: "/((?!_next/static|_next/image|api).*)",
      headers: [
        {
          key: "Cache-Control",
          value: "public, max-age=0, s-maxage=3600, stale-while-revalidate=86400",
        },
      ],
    },
    {
      // Oturuma bağlı sayfalar hiç önbelleklenmez.
      source: "/:yol(admin|hesabim)/:kalan*",
      headers: [{ key: "Cache-Control", value: "no-store, must-revalidate" }],
    },
  ];
}

Üç değerin anlamı:

  • max-age=0 — tarayıcı önbelleğe almasın, her seferinde sorsun.
  • s-maxage=3600 — CDN bir saat taze saysın.
  • stale-while-revalidate=86400 — bir saat dolduktan sonra CDN eski kopyayı sunmaya devam etsin ama arka planda yenisini çeksin. Ziyaretçi bekleme yaşamaz, hız kaybı olmaz.

Sonuç: dağıtımdan en geç bir saat sonra herkes güncel içeriği görür, sayfa hızından ödün vermeden.

Süreyi kendi dağıtım sıklığınıza göre seçin:

Dağıtım sıklığı Önerilen s-maxage
Günde birkaç kez 300 (5 dakika)
Günde bir 3600 (1 saat)
Haftada bir 21600 (6 saat)
Ayda bir 86400 (1 gün)

Daha kısa süre daha çok istek demek — sunucunuzun yükünü artırır. stale-while-revalidate bu maliyeti büyük ölçüde karşılıyor: süre dolduğunda ziyaretçi beklemez, arka planda tazeleme yapılır.

Dikkat: _next/static hariç tutulmalı

Yukarıdaki kuralda _next/static bilinçli olarak dışarıda. O dosyaların adı içeriğinin özetini taşır (chunk-a3f9b2.js gibi); içerik değişince ad da değişir. Yani o dosyalar gerçekten hiç değişmez ve bir yıl önbelleklenmeleri doğrudur:

cache-control: public, max-age=31536000, immutable

Bu ayrımı yapmazsanız her dağıtımda tüm JavaScript ve CSS'i yeniden indirtirsiniz — sitenizi yavaşlatırsınız.

Aynı mantık public/ altındaki görseller için geçerli değildir. kapak.jpg adı içeriğinin özetini taşımaz; aynı adla yeni bir görsel yüklerseniz eski dosya önbellekte kalır. İki çözüm var: dosya adına sürüm ekleyin (kapak-v2.jpg) ya da bu klasöre daha kısa bir süre verin. Görselleri hazırlarken uyguladığımız adımları görsel boyutu yazısında topladık.

Bizim atladığımız ikinci tuzak

Kuralı yazarken admin panelini kural dışında bırakmıştık. Sonuç: admin giriş sayfası CDN'de bir yıl önbelleklendi.

Oturuma bağlı bir ekranın paylaşımlı önbellekte durması iki soruna açık kapı bırakır: yanlış kullanıcıya sayfa gösterilmesi ve çıkış yaptıktan sonra eski ekranın sunulması. Admin ve üyelik alanlarına açıkça no-store verin — hariç tutmak yetmez, çünkü hariç tutulduğunda Next'in kendi bir yıllık varsayılanı geçerli kalır.

Aynı kural şu yollar için de geçerli: sepet, ödeme adımları, sipariş takibi, müşteri portalı, hesap ayarları, form gönderim sonuç sayfaları. Kural şu: çıktısı kullanıcıya göre değişen hiçbir sayfa paylaşımlı önbelleğe girmemeli.

Emin olamadığınız bir sayfa için basit bir test var: aynı adresi bir gizli pencerede, oturum açmadan açın. Gördüğünüz şey oturum açmış halinizle aynıysa önbelleklenebilir; değilse no-store verin.

CDN'e göre ne değişir

Cloudflare. Varsayılan olarak yalnızca statik dosya uzantılarını önbelleğe alır; HTML'i önbelleğe almak için "Cache Everything" kuralı gerekir. Yani sorunu yaşıyorsanız muhtemelen o kuralı biri açmıştır. Dağıtım sonrası temizleme için API'si var ve dağıtım betiğine tek satırla eklenebilir:

curl -sX POST "https://api.cloudflare.com/client/v4/zones/$ZONE/purge_cache" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"purge_everything":true}'

Paylaşımlı hosting CDN'leri. Genelde ne kural yazabilirsiniz ne de güvenilir bir temizleme düğmesi vardır. Tek yolunuz uygulamadan doğru başlığı vermek. Paylaşımlı hosting yazısında bu ve benzeri sınırları uzun uzun anlattık.

Kendi nginx önbelleğiniz. Kontrol tamamen sizde; dağıtım betiğinize önbellek klasörünü temizleyen bir satır ekleyebilirsiniz. Ama unutmayın: önbelleği silmek, s-maxage değerini düzeltmenin yerine geçmez. İkisini birden yapın.

Kendi sitenizde nasıl kontrol edersiniz

Terminalde:

curl -sI https://siteniz.com/ | grep -i "cache-control\|^age\|x-cache\|cf-cache-status"

s-maxage değeri binlerce saniye çıkıyorsa ve CDN kullanıyorsanız, aynı sorun sizde de var demektir.

Başlıkların ne dediğini okumak:

  • age: 20031 → kopya 5,5 saattir önbellekte
  • cf-cache-status: HIT → Cloudflare önbellekten verdi
  • cf-cache-status: DYNAMIC → önbelleğe alınmadı, sunucudan geldi
  • x-cache: HIT → başka bir CDN önbellekten verdi

Bir sayfanın gerçekten güncel olup olmadığını anlamak için önbelleği baypas edin:

curl -s "https://siteniz.com/?rastgele=$RANDOM" | grep "aradığınız-yeni-metin"

Sorgu dizesi farklı olduğu için CDN yeni bir kayıt sayar ve sunucudan taze içerik çeker. Normal adresle farklı sonuç alıyorsanız teşhis kesinleşir.

Dağıtımdan sonra tüm siteyi tarayıp kaç sayfanın eski olduğunu saymak için:

curl -s https://siteniz.com/sitemap.xml | grep -oE 'https://[^<]+' \
  | while read u; do
      yas=$(curl -sI "$u" | grep -i '^age:' | tr -dc '0-9')
      [ -n "$yas" ] && [ "$yas" -gt 3600 ] && echo "$yas  $u"
    done

Çıktıdaki her satır, bir saatten uzun süredir tazelenmemiş bir sayfa. Biz bu listeyi ilk çalıştırdığımızda 50 satır gelmişti.

Dağıtım sonrası rutin

Her dağıtımdan sonra otuz saniyelik bir kontrol, bu yazıdaki sorunun size hiç ulaşmamasını sağlar. Bizim kullandığımız kısa betik:

#!/bin/bash
SITE="https://siteniz.com"
ARANAN="$1"   # yeni sürümde kesin bulunması gereken bir metin

echo "── Sunucu (önbellek baypas)"
curl -s "$SITE/?x=$RANDOM" | grep -c "$ARANAN"

echo "── Normal istek"
curl -s "$SITE/" | grep -c "$ARANAN"

echo "── Başlıklar"
curl -sI "$SITE/" | grep -iE "cache-control|^age|cf-cache-status"

echo "── Ana sayfa yanıt süresi"
curl -s -o /dev/null -w "%{time_total}s\n" "$SITE/"

Aranan metin olarak sürüm numarası, yeni bir başlık ya da yeni eklediğiniz bir cümleyi verin. İki sayı da 1 dönüyorsa dağıtım gerçekten yayında.

Bu kontrolü dağıtım betiğinizin son adımı yapın. Elle yapılan kontrol, yapılmayan kontroldür.

Sık yapılan dört hata

1. Sadece CDN'i temizleyip başlıkları düzeltmemek. Bir sonraki dağıtımda aynı sorun geri gelir.

2. Her şeye no-store vermek. Sorunu çözer, sitenizi yavaşlatır. Önbellek düşmanınız değil, yanlış ayarlanmış önbellek düşmanınız.

3. _next/static süresini de kısaltmak. Her dağıtımda tüm JavaScript yeniden iner.

4. Oturumlu sayfaları unutmak. Yalnızca performans değil, güvenlik sorunu.

Önbelleklenmemesi gereken dosyalar

HTML sayfaların dışında, uzun süre önbellekte kalması özellikle zarar veren dört dosya var.

sitemap.xml. Yeni yayınladığınız içerik sitemap'e girer ama CDN eski kopyayı sunuyorsa Google onu göremez. Sitemap'iniz kusursuz olsa bile dışarıya eski hali gider.

robots.txt. Bir botu yanlışlıkla bloklamışsanız ve düzeltmişseniz, düzeltmenin yayılması gerekir. Ters durumu düşünün: bir kural eklediniz ama eski dosya sunuluyor — kural hiç yürürlüğe girmemiş olur.

llms.txt ve ai.txt. Aynı mantık. Bu dosyaların amacı zaten güncel bir yol haritası sunmak; bir haftalık kopya sunmak amacı bozar.

RSS beslemesi. Abonelerin yeni yazıyı görmemesi demek.

Bu dosyalara bir saatten uzun süre vermeyin:

Cache-Control: public, max-age=0, s-maxage=600

On dakika, hem sunucuyu yormaz hem de güncellemenin makul sürede yayılmasını sağlar.

Kontrol tek satır:

for f in /sitemap.xml /robots.txt /llms.txt /rss.xml; do
  echo "$f → $(curl -sI https://siteniz.com$f | grep -i '^cache-control' | tr -d '\r')"
done

Keşif katmanı dosyalarının ne işe yaradığını ve nasıl üretildiğini llms.txt yazısında ayrıntılı anlattık.

Kontrol listesi

  • HTML sayfaları s-maxage makul bir değerde (300-3600)
  • stale-while-revalidate eklenmiş
  • _next/static hâlâ immutable ve bir yıllık
  • Admin, portal, sepet gibi yollar no-store
  • Sitemap ve robots.txt uzun süre önbelleklenmiyor
  • Dağıtım betiği CDN temizleme çağrısı yapıyor (mümkünse)
  • Dağıtım sonrası age başlığı taranıyor

Özet

Vercel'de görünmeyen bir sorun, Vercel dışında sessizce çalışır. Next'in bir yıllık varsayılanı, dağıtımda otomatik temizlenen bir CDN varsayar. O varsayım geçerli değilse önbellek süresini kendiniz belirlemeniz gerekir.

Dağıtımdan sonra "acaba yayına girdi mi" diye tereddüt ediyorsanız, ölçün. Biz ölçmeseydik sitenin yarısının eski kaldığını fark etmeyecektik.

Fiyat ya da stok bilgisi gösteren bir sitede bu mesele doğrudan paraya dönüşür: kampanya bitti ama sayfa hâlâ eski fiyatı gösteriyor. E-ticaret sitesi kurulumunda önbellek stratejisini baştan kurmanın gerekçesi bu. Alan adınızı Cloudflare'e yeni taşıdıysanız, proxy açıldıktan sonra bu başlıkların davranışının değiştiğini de hesaba katın — geçiş yazısında sıraladığımız adımlardan biri de buydu.

Etiketler
CDN önbelleks-maxagestale-while-revalidateNext.js cache-controldağıtım sonrası eski içerikcache invalidation

Sıkça sorulan sorular

Vercel her dağıtımda kendi CDN önbelleğini otomatik temizler. Next.js'in `s-maxage=31536000` (bir yıl) varsayılanı tam olarak bu davranışı varsayar. Hostinger, Cloudflare ya da kendi kurduğunuz nginx önbelleği dağıtımınızdan haberdar olmadığı için o bir yıllık süreyi harfiyen uygular.

Çoğu kurumsal site için 3600 (bir saat) iyi bir başlangıç. Yanına `stale-while-revalidate=86400` eklerseniz süre dolduktan sonra CDN eski kopyayı sunmaya devam ederken arka planda yenisini çeker — ziyaretçi bekleme yaşamaz. Günde birkaç kez dağıtım yapıyorsanız 300 saniyeye kadar düşürebilirsiniz.

Hayır. O dosyaların adı içeriğinin özetini taşır; içerik değişince dosya adı da değişir. Dolayısıyla gerçekten hiç değişmezler ve `max-age=31536000, immutable` doğru ayardır. Onları da kısaltırsanız her dağıtımda tüm JavaScript ve CSS yeniden indirilir, siteniz yavaşlar.

`curl -sI https://siteniz.com/ | grep -i 'cache-control\|^age'` komutunu çalıştırın. `age` başlığı, o kopyanın önbellekte kaç saniyedir durduğunu söyler. Taze içeriği görmek için adrese rastgele bir sorgu parametresi ekleyin — CDN onu yeni bir kayıt sayar ve sunucudan çeker.

+90 551 847 1997