# 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.

- Kanonik URL: https://algoritmaajans.com/tr/blog/cdn-onbellegi-nasil-temizlenir
- Dil: tr
- Kategori: Yazılım
- Yayın: 2026-08-27
- Güncelleme: 2026-08-29
- Yayıncı: Algoritma Ajans

---

**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](/images/blog/cdn-eski-icerik-inline-1.jpg)

## 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](/tr/blog/sitemap-lastmod-nedir-nasil-kullanilir). İ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ı:

```bash
# 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:

```ts
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](/tr/blog/web-icin-gorsel-boyutu-nasil-kucultulur) 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:

```bash
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](/tr/blog/paylasimli-hostinge-nextjs-kurulur-mu) 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:

```bash
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:

```bash
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:

```bash
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:

```bash
#!/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:

```bash
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](/tr/blog/llms-txt-nedir-nasil-hazirlanir) 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](/tr/blog/e-ticaret-sitesi-nasil-kurulur) ö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](/tr/blog/cloudflare-dns-gecisi-nasil-yapilir) sıraladığımız adımlardan biri de buydu.

<!-- GORSEL: cdn-eski-icerik-inline-2 — Bir isteğin tarayıcı, CDN düğümü ve sunucu arasındaki yolculuğu; her durakta hangi cache-control direktifinin geçerli olduğu etiketli -->
