ERETEK
BİLGİ SİSTEMLERİ

Cache-Control Başlıkları: CDN ve Tarayıcı Önbelleğini Doğru Ayarlamak

max-age, s-maxage, no-cache, no-store, stale-while-revalidate, ETag ve Vary: içerik türüne göre Cache-Control setleri, önbellek kaçakları ve curl ile doğrulama.

Bu makalede7 bölüm
Teknik özet
Teknoloji
HTTP Caching (RFC 9111), Cache-Control, ETag, Vary, CDN, Nginx, Apache, curl
Konu
HTTP önbellek başlıklarının içerik türüne göre yapılandırılması
Seviye
Orta
Ön koşullar
  • HTTP istek/yanıt yapısı bilgisi
  • Web sunucusu veya CDN yapılandırmasına erişim
  • Komut satırında curl kullanımı

İçerik güncellendi ama ziyaretçi hâlâ eski sayfayı görüyor; ya da tersi, CDN var ama her istek kaynak sunucuya gidiyor. İki şikâyetin kökü çoğu zaman aynı yerdedir: Cache-Control başlığı. Bir yanıt ziyaretçiye ulaşana kadar tarayıcı önbelleği, CDN ve varsa reverse proxy’den geçer; her katmanın ne kadar saklayacağına ve ne zaman yeniden doğrulayacağına bu başlık karar verir. Bu rehber HTTP önbellekleme semantiğini RFC 9111 temelinde anlatıyor ve statik varlıklar, HTML ve oturumlu sayfalar için araçtan bağımsız başlık setleri öneriyor.

HTTP önbellek katmanları

RFC 9111 önbellekleri iki türe ayırır. Özel (private) önbellek tek bir kullanıcıya aittir; tarayıcı önbelleği tipik örnektir. Paylaşımlı (shared) önbellek birden fazla kullanıcıya yanıt sunar; CDN ve reverse proxy bu gruptadır. Ayrımın pratik sonucu net: kullanıcıya özel içerik (hesap sayfası, sepet) tarayıcıda saklanabilir, paylaşımlı önbellekte asla saklanmamalıdır. Bu çizgi aşılırsa bir kullanıcının sayfası diğerine gider.

Bir yanıtın önbellekteki ömrü iki kavramla yönetilir:

  • Tazelik (freshness): Yanıtın kaynak sunucuya sorulmadan kullanılabileceği süre. max-age veya s-maxage ile belirlenir. Önbellekten sunulan yanıtın ne kadar süredir saklandığını Age başlığı gösterir.
  • Doğrulama (validation): Tazelik süresi dolduğunda önbelleğin kaynak sunucuya “bu hâlâ geçerli mi?” diye sorması. Sunucu ETag veya Last-Modified ile karşılaştırma yapar; içerik değişmemişse gövdesiz 304 Not Modified döner.

Cache-Control yönergeleri tablosu

Sık kullanılan yanıt yönergeleri ve anlamları aşağıda. stale-while-revalidate ve stale-if-error RFC 5861’de, immutable RFC 8246’da tanımlanmıştır; diğerleri RFC 9111’dedir.

Yönerge Anlamı Dikkat
max-age=N N saniye taze kabul edilir Tüm önbellekler için geçerli
s-maxage=N Yalnız paylaşımlı önbellekler için tazelik CDN’de max-age’i geçersiz kılar
no-cache Saklanabilir, her kullanımda doğrulanmalı “Önbellekleme yok” demek değildir
no-store Hiçbir önbellekte saklanmaz Hassas veri için; geri tuşu performansını etkiler
private Yalnız tarayıcı saklayabilir Kişiye özel içerik
public Paylaşımlı önbellek de saklayabilir Kimlik doğrulamalı yanıtlarda bile saklamaya izin verir
must-revalidate Bayatladıktan sonra doğrulanmadan sunulamaz Kaynak erişilemezse hata döner
stale-while-revalidate=N Bayat yanıt N saniye sunulurken arka planda yenilenir Destek tarayıcı ve CDN’e göre değişir
immutable Tazelik süresince içerik değişmez Yalnız sürümlü dosya adlarında

Expires başlığı eski bir mekanizmadır ve max-age varsa yok sayılır. Pragma: no-cache yalnız HTTP/1.0 uyumluluğu için vardır; modern yapılandırmada gerekmez. Bazı CDN’ler RFC 9213 ile tanımlanan CDN-Cache-Control başlığını destekler; bu başlık tarayıcıya giden Cache-Control değerine dokunmadan yalnız CDN davranışını ayarlar. Sahada en sık karıştırılan ikili ise no-cache ile no-store’dur. Biri doğrula der, diğeri sakla(ma) der.

Statik varlıklar için uzun süreli önbellek

Web performansı açısından CSS, JavaScript, font ve görsel dosyalarında en verimli strateji şudur: içerik değiştiğinde URL de değişsin, dosya çok uzun süre önbellekte kalsın. Buna “fingerprint” veya önbellek kırma (cache busting) denir: app.3f9a1c.css gibi dosya adına içerik özeti eklenir ya da style.css?ver=2.4.0 gibi sürüm parametresi kullanılır. URL değişince önbellek anahtarı da değişir ve kullanıcı yeni dosyayı alır.

Cache-Control: public, max-age=31536000, immutable

Nginx’te bu başlık uzantıya göre şöyle verilir:

location ~* \.(?:css|js|woff2|avif|webp|jpg|jpeg|png|svg)$ {
    add_header Cache-Control "public, max-age=31536000, immutable" always;
    access_log off;
    try_files $uri =404;
}

Apache’de mod_headers etkinse:

<FilesMatch "\.(css|js|woff2|avif|webp|jpe?g|png|svg)$">
    Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>

Bu kuralın bir ön koşulu var: uzantıya göre uzun önbellek verilen her dosyanın URL’si, içeriği değiştiğinde değişmelidir. WordPress’te tema ve eklenti dosyaları ?ver= parametresiyle sürümlenir; ancak medya kütüphanesinde bir görseli aynı adla değiştiren eklentiler veya sürüm parametresi olmadan elle yüklenen dosyalar bu varsayımı bozar. Sonuç: bir yıl boyunca eski logo. Nginx’te iç içe location blokları ve add_header devralma kuralları beklenmedik sonuç verebilir; alt blokta tek bir add_header tanımlamak üst bloktaki tüm add_header satırlarını geçersiz kılar. Yapılandırmayı her değişiklikten sonra başlık kontrolüyle doğrulayın; reverse proxy katmanındaki başlık yönetimi için Nginx reverse proxy rehberine bakabilirsiniz.

HTML ve dinamik içerik stratejisi

HTML’in URL’si değişmediği için uzun tarayıcı önbelleği verilemez; verilirse içerik güncellemesi kullanıcıya günlerce ulaşmaz. Dengeli yaklaşım, tarayıcıda her seferinde doğrulama yapmak ve CDN’de kısa bir tazelik süresi tanımlamaktır:

# Oturumsuz ziyaretçiler için herkese açık HTML
Cache-Control: public, max-age=0, s-maxage=600, stale-while-revalidate=60

# Kişiye özel sayfalar (hesap, sepet, ödeme)
Cache-Control: private, no-cache

# Hassas veri içeren yanıtlar
Cache-Control: no-store

Birinci satırda tarayıcı sayfayı her ziyarette doğrular; CDN ise 10 dakika boyunca kaynak sunucuya gitmeden sunar, süre dolduktan sonraki 60 saniye içinde gelen isteklere bayat kopyayı verirken arka planda yeniler. İçerik yayınlandığında 10 dakikayı beklememek için yayın iş akışına ilgili URL’lerin purge (temizleme) işlemini bağlayın: güncellenen yazı, ana sayfa, kategori arşivi ve varsa sitemap. Bazı CDN’ler etiket bazlı temizleme sunar; bu özellik plan ve sağlayıcıya göre değişir. Purge yoksa TTL kısaltmak çözüm değildir. Sadece isabet oranını düşürür.

Yönlendirmeler sık gözden kaçar. 301 yanıtları açık bir önbellek başlığı olmasa bile varsayılan olarak önbelleğe alınabilir ve tarayıcılar bunları uzun süre saklayabilir. Yanlış bir 301’i sunucuda düzeltmek, onu daha önce almış tarayıcıları düzeltmez. Taşıma veya yeniden yapılandırma sırasında kalıcılığından emin olmadığınız yönlendirmeler için önce 302 kullanın veya açık bir max-age verin.

Vary, çerezler ve önbellek kaçakları

Vary başlığı, önbelleğe aynı URL için hangi istek başlıklarına göre ayrı kopyalar tutması gerektiğini söyler. Vary: Accept-Encoding, sıkıştırılmış ve sıkıştırılmamış kopyaların ayrılması için standarttır. Vary: User-Agent veya Vary: Cookie ise neredeyse her isteği tekil hale getirir ve paylaşımlı önbellekte isabet oranını çökertir. CDN’lerin Vary desteği de sınırlıdır; çoğu yalnız belirli değerleri işler.

Asıl risk, kişiye özel bir yanıtın paylaşımlı önbelleğe yazılıp başka kullanıcılara sunulmasıdır. Bu “önbellek kaçağı” performans sorunu değil, veri sızıntısıdır. Tipik nedenleri:

  • Oturum açmış kullanıcıya dönen HTML’de public veya s-maxage bulunması.
  • Yanıtta Set-Cookie varken CDN kuralının çerezi yok sayıp yanıtı saklaması.
  • Önbelleği atlama kuralında oturum çerezlerinin eksik listelenmesi. WordPress için wordpress_logged_in_, wp-postpass_ ve comment_author_ önekli çerezler; WooCommerce için woocommerce_items_in_cart ve wp_woocommerce_session_ önekli çerezler tipik örneklerdir.
  • Ön yüzde kişiselleştirilmiş parçaların (sepet sayacı, kullanıcı adı) HTML içine sunucu tarafında basılması.

Son madde için kalıcı çözüm, kişisel parçaları HTML’den ayırıp ayrı ve önbelleklenmeyen bir istekle doldurmaktır. E-ticaret sitelerindeki tipik senaryolar için WooCommerce cache hataları rehberine, sayfa önbelleği ile CDN ve nesne önbelleğinin birlikte çalışması için Cloudflare, WP Rocket ve Redis rehberine bakabilirsiniz.

Başlıkları doğrulama ve sorun giderme

Kurumsal web altyapısında önbellek yapılandırmasını tarayıcıda değil, komut satırında doğrulayın. Tarayıcı kendi önbelleği ve eklentileri yüzünden yanıltıcı sonuç verebilir. Bazı sunucular HEAD isteğine GET’ten farklı başlık döndürdüğü için GET isteği yapıp gövdeyi atmak daha güvenilirdir:

# Yanıt başlıklarını göster, gövdeyi at
curl -s -o /dev/null -D - https://www.ornek.com/

# Sıkıştırma kabul eden bir tarayıcı gibi iste
curl -s -o /dev/null -D - -H 'Accept-Encoding: br, gzip' https://www.ornek.com/assets/app.css

# Koşullu doğrulama: önceki yanıttaki ETag ile 304 beklenir
curl -s -o /dev/null -D - -H 'If-None-Match: "5f2a-63b1c9e0"' https://www.ornek.com/

# Oturum çereziyle istek: önbellekten gelmemeli
curl -s -o /dev/null -D - -H 'Cookie: wordpress_logged_in_test=1' https://www.ornek.com/

Çıktıda kontrol edilecek başlıklar ve yorumları:

Başlık Ne gösterir
Cache-Control Kaynağın veya CDN’in uyguladığı politika
Age Yanıtın paylaşımlı önbellekte kaç saniyedir bulunduğu
ETag / Last-Modified Doğrulama yapılabilir mi
Vary Önbellek anahtarına giren istek başlıkları
Sağlayıcıya özel durum başlığı İsabet durumu; ör. Cloudflare’de cf-cache-status (HIT, MISS, EXPIRED, BYPASS, DYNAMIC)

Kontrol sırası basit. Aynı isteği art arda iki kez gönderin: ikinci yanıtta isabet görülmüyor ve Age artmıyorsa CDN yanıtı saklamıyordur; önce kaynak sunucunun gönderdiği başlığa, sonra CDN kuralına bakın. Ardından aynı isteği oturum çereziyle tekrarlayın. Oturum çerezli istekte isabet görülüyorsa ciddi bir önbellek kaçağı riski vardır. Bunu ilk iş düzeltin.

Kurumsal WordPress sitelerinde sayfa önbelleği, CDN kuralları ve başlık politikalarının birlikte tasarlanması için WordPress cache ve CDN optimizasyonu hizmetimizi inceleyebilirsiniz. Tüm yönergelerin normatif tanımı RFC 9111’de yer alır.

Sık sorulan sorular

no-cache önbelleğe alınmaz anlamına mı gelir?

Hayır. no-cache, yanıtın önbelleğe alınabileceğini ama her kullanımdan önce kaynak sunucuyla doğrulanması gerektiğini söyler. Doğrulama ETag veya Last-Modified ile yapılır; içerik değişmemişse sunucu gövdesiz 304 yanıtı döner. Yanıtın hiçbir önbellekte saklanmaması isteniyorsa no-store kullanın.

Cache-Control başlığı hiç gönderilmezse ne olur?

RFC 9111’e göre açık bir tazelik bilgisi yoksa önbellekler belirli durum kodlarında sezgisel (heuristic) tazelik hesaplayabilir; Last-Modified başlığı varsa dosyanın yaşına göre bir süre belirlenir. Yani aynı dosya farklı tarayıcı ve CDN’lerde öngörülemeyen sürelerle saklanır. Her yanıt türü için açık bir Cache-Control değeri tanımlayın; belirsizlik ancak böyle kalkar.

immutable yönergesi ne işe yarar?

immutable, yanıtın tazelik süresi boyunca değişmeyeceğini belirtir. Destekleyen tarayıcılar kullanıcı sayfayı yenilediğinde bile bu kaynaklar için koşullu doğrulama isteği göndermez. Yalnız dosya adında sürüm veya özet taşıyan varlıklarda kullanın; aynı URL’de içeriği değişen bir dosyaya verilirse kullanıcılar eski sürümü görmeye devam eder.

CDN’de HTML’i önbelleğe almak güvenli mi?

Oturum açmamış ziyaretçilere aynı içeriğin gösterildiği sayfalar için güvenlidir ve önbellek isabet ettiğinde TTFB genellikle belirgin biçimde iyileşir. Şartları şunlardır: oturum, sepet ve kişiselleştirme çerezi taşıyan istekler önbelleği atlamalı, Set-Cookie içeren yanıtlar paylaşımlı önbelleğe yazılmamalı ve içerik güncellendiğinde ilgili URL’ler temizlenmelidir.

Teknik özet
Teknoloji
HTTP Caching (RFC 9111), Cache-Control, ETag, Vary, CDN, Nginx, Apache, curl
Konu
HTTP önbellek başlıklarının içerik türüne göre yapılandırılması
Seviye
Orta
Ön koşullar
  • HTTP istek/yanıt yapısı bilgisi
  • Web sunucusu veya CDN yapılandırmasına erişim
  • Komut satırında curl kullanımı
Eretek uzman desteği

Bu konu sizin ortamınızda nasıl çalışır, birlikte bakalım.

Mevcut yapınızı inceliyor, darboğazı tespit ediyor ve uygulanabilir bir iş planı öneriyoruz.