Hiç başınıza geldi mi? Trafiğin şenlendiği bir akşam, sitenin tam da kampanya sayfası açıldığı an, sunucu bir anlığına tekler. Telefon çalmaya başlar, Slack susmaz, izleme panosu kıpkırmızı. O sırada aklımdan hep şu geçer: Keşke tarayıcıya, “Bir dakika sakin ol, bende hâlâ güzel bir kopya var, onu göstereyim, arkada tazelemeye çalışayım” diyebilsem. İşte tam bu noktada stale-while-revalidate ve stale-if-error devreye giriyor. Basit birkaç başlık, ama doğru yerde doğru ayarla, o kısa kesintileri kullanıcı neredeyse fark etmez hale getiriyor.
Bu yazıda sizi teknik jargona boğmadan, ama sahada işinizi görecek netlikte bir akışla götürmek istiyorum. Önce bu iki sihirli direktifin mantığını konuşacağız. Sonra bunu Nginx’te nasıl hayata geçiririz, Cloudflare tarafında neleri ayarlarız, WordPress sitelerde hangi küçük dokunuşlarla büyük fark yaratırız, adım adım gideceğiz. Arada gerçek hayattan ufak anekdotlar var; o ünlü pazar akşamı kesintisini nasıl tatlıya bağladığımı da anlatacağım. Hadi başlayalım.
İçindekiler
- 1 stale-while-revalidate ve stale-if-error Nedir? Neden Umursamalıyız?
- 2 HTTP Başlıklarıyla Temel Tarif: Nasıl Yazılır, Nasıl Okunur?
- 3 Nginx’te stale-while-revalidate ve stale-if-error: Sahada İşleyen Tarif
- 4 Cloudflare’de Pratik Ayarlar: Kenarda Kalkan, Ortada Dans
- 5 WordPress’te Dayanıklı Cache: Eklenti mi, functions.php mi, Sunucu mu?
- 6 Gerçek Dünya Senaryosu: Bakım Anı, Sıcak Deploy ve Eski Dost “Stale”
- 7 Doğru Süreleri Seçmek: Fazla Bayat, Az Taze Dengesini Kurmak
- 8 Gözlem ve Sorun Giderme: “Cache Çalışıyor mu?” Diyen İçin Küçük Araç Çantası
- 9 Gelişmiş İpuçları: İnce Ayarlarla Tatlandırmak
- 10 Sık Karşılaşılan Hatalar: Neleri Yapmayalım?
- 11 Kapanış: Küçük Bir Başlık, Büyük Bir Huzur
stale-while-revalidate ve stale-if-error Nedir? Neden Umursamalıyız?
Bir dakikalık sabır, mutlu kullanıcı
Önbellek dediğimiz şey, sunucunun ürettiği sayfanın bir kopyasını kenarda tutmak demek. Normalde bu kopyanın bir süresi vardır, biter. Bittiği an yeni içerik istenir. Ama ya o sırada sunucu meşgulse? Ya veritabanı biraz gecikiyorsa, ya da ağda minik bir aksilik olduysa? Tam da burada stale-while-revalidate devreye giriyor: Kopya “resmen” eskimiş olsa bile, tarayıcıya “Bu eski içeriği şimdi göster, ben arkada tazeleyeceğim” der. Kullanıcı beklemez, site hızlı görünür. Siz ise arkada yeni içeriği hazırlarsınız.
stale-if-error ise farklı bir ruh haline hitap eder. Sunucu gerçekten hata veriyorsa, yani yeni içeriği getiremiyorsak, “Eski ama çalışır sayfayı göster” der. Bir nevi emniyet yastığı gibi düşünün. Kısa bir kesinti, bir deploy’ın ilk saniyelerindeki tökezleme, veya beklenmedik bir 502… Hepsini kullanıcıdan saklamak değil de, ona pürüzsüz bir deneyim sunmak gibi.
İkisi birden kullanıldığında büyü tamamlanır. Özetle, stale-while-revalidate hızlı hissiyatı, stale-if-error ise dayanıklılığı artırır. Mesela şöyle düşünün: Sayfanın ömrünü 60 saniye yaptınız. Bu 60 saniye doldu, ama arka tarafta yoğunluk var. Tarayıcı, “Eski kopyayı göstereyim, arka planda yenisini isteyeyim” der ve kimse “bekleme” ekranı görmez. İşler sarpa sararsa, “hata alana kadar” eski kopya devreye girer.
HTTP Başlıklarıyla Temel Tarif: Nasıl Yazılır, Nasıl Okunur?
Bir dilim Cache-Control, üstüne iki kaşık stale
Bu işin kalbi, Cache-Control başlığı. Tarayıcıya ve aradaki ara katmanlara (CDN, reverse proxy) şu şekilde konuşuruz:
Cache-Control: public, max-age=60, stale-while-revalidate=300, stale-if-error=86400
Burada 60 saniye boyunca içerik taptaze. 60 saniye dolunca, 300 saniye daha eski kopyayı göstermeye izin var ama arka planda yeni kopya istenir. Eğer sunucu hata verirse, 1 gün boyunca (86400 saniye) eski kopyayı göstermeyi dene diyoruz. Bu değerler tamamen senaryoya göre değişir; kritik sayfalarda süreler kısa, statik içeriklerde daha uzun tutulabilir.
Bu başlıkları anlamak için derin teknik bilgi şart değil. Bir sayfanın “resmi” ömrü ve “eski ama işe yarar” ömrü var gibi düşünebilirsiniz. İkinci ömür, en çok gerektiği anlarda devreye girer. Özellikle kampanya sayfalarında, blog anasayfasında, “popüler ürünler” gibi yüksek trafikli ama çok sık değişmeyen bölümlerde harika sonuç verir.
Detay merak edenler için not: Bu davranışlar HTTP standartlarında anlatılıyor. Yön gösterici olarak RFC 5861’deki stale uzantıları ve MDN Cache-Control dökümantasyonu çok açıklayıcı. Ama gelin, lafı uzatmadan sahaya inelim.
Nginx’te stale-while-revalidate ve stale-if-error: Sahada İşleyen Tarif
Proxy cache ve birkaç küçük incelik
Nginx, doğru ayarla bu işi çok güzel yapıyor. Klasik bir reverse proxy kurulumunda, küçük bir cache alanı ve bazı kurallar ile hem hız kazanırsınız hem de kesinti anlarında kullanıcıyı üzmezsiniz. Benim pratikte sık kullandığım bir iskelet şöyle olur:
# Cache alanını tanımlayın (yol, anahtar, boyut, inaktif süre vb.)
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=MAINCACHE:100m max_size=10g inactive=30m use_temp_path=off;
# Giriş yapan kullanıcılar veya özel çerezler için cache'i bypass etmek isteyebilirsiniz
map $http_cookie $bypass_cache {
default 0;
~*(wordpress_logged_in|comment_author|woocommerce_items_in_cart) 1;
}
server {
listen 80;
server_name ornek.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Cache anahtarı ve kontrolü
proxy_cache MAINCACHE;
proxy_cache_key "$scheme$proxy_host$request_uri";
proxy_cache_bypass $bypass_cache;
proxy_no_cache $bypass_cache;
# Hata anında bayat içeriği göster
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
# Origin'den gelen Cache-Control'ü geçirme veya override etme
# Eğer backend’iniz doğru başlıkları üretiyorsa, aşağıdaki add_header gerekmez.
add_header Cache-Control "public, max-age=60, stale-while-revalidate=300, stale-if-error=86400" always;
# Cache durumu gözlemlemek için header eklemek güzel olur
add_header X-Cache-Status $upstream_cache_status;
}
}
Burada dikkat edilecek nokta, proxy_cache_use_stale ve proxy_cache_background_update ayarları. İlki hata veya timeout durumunda eldeki kopyayı gösterir. İkincisi ise güncelleme talebini arka plana atarak, yeni isteklere eski kopyayı hızlıca servis eder. Böylece “hmm, bir yavaşlık var” hissi yerine, “site akıyor” hissi oluşur. Nginx’in bu yöndeki yeteneklerini merak ederseniz, resmi proxy cache belgeleri aklınızın bir köşesinde dursun.
Bir sahne anısı: Bir ürün lansmanı akşamı, beklenenden çok trafik geldi. Veritabanı çekirdekleri biraz terledi. Nginx log’larında ufak time-out’lar görüyordum. Buna rağmen kullanıcı şikâyeti neredeyse hiç gelmedi. Çünkü stale-while-revalidate ile sayfalar akmaya devam etti, arka planda veritabanı kendine geldi. Kampanya bitene kadar her şey bir tık daha “cool” göründü.
Tek uyarı: Kişiselleştirilmiş sayfalarla dikkatli olun. Kullanıcıya özel sepet, profil, dashboard gibi alanları cache’e dahil etmeyin. Çerezlere göre bypass etmek, ya da bu sayfalara “Cache-Control: private, no-store” gibi başlıklar vermek iyi bir alışkanlıktır.
Cloudflare’de Pratik Ayarlar: Kenarda Kalkan, Ortada Dans
Edge’de hız, arka planda zarif tazeleme
Cloudflare kullanıyorsanız iş daha da keyifli. Çünkü edge tarafında, “origin’in dediğini dinle” prensibiyle, yani sizin Cache-Control başlıklarınıza saygı duyarak çalıştırabilirsiniz. En temiz yol, uygulamanızın doğru başlıkları üretmesi. Cloudflare bunları görür, uygular, gerekli yerde bayat içeriği gösterir.
Pratikte, “Cache Rules” veya eski Page Rules tarafında “Cache Level: Cache Everything” gibi ayarları sadece gerçekten gerekliyse kullanın. Genelde “Origin Cache-Control”a sadık kalmak daha sağlıklıdır. Örneğin ürün listesi sayfalarına public, max-age=60, stale-while-revalidate=300, stale-if-error=86400 başlığını verin; Cloudflare edge bunu uygular ve kısa süreli origin teklemelerinde kullanıcıyı mutlu eder.
Bir de göz ardı edilmemesi gereken küçük incelikler var. Örneğin query string’ler ve cookie’ler. Cloudflare çoğu zaman query string’i cache anahtarına dahil eder. Eğer UTM parametreleri gibi pazarlama izleri, cache’i gereksiz parçalara bölüyorsa, Cache Rules ile bu parametreleri yoksaymak bazen harika sonuç verir. Ama dikkatli olmakta fayda var; aynı sayfanın farklı varyasyonlarını yanlışlıkla tek potada eritmeyin.
Cloudflare tarafıyla ilgili daha canlı örnekler seviyorsanız, edge bağlantılarını “hep açık” tutmaya dair pratik notlar için şu yazıya göz atabilirsiniz: Cloudflare ile WebSocket ve gRPC yayını nasıl hep canlı kalır? Burada konuştuğumuz süreklilik fikri, cache dayanıklılığıyla çok güzel örtüşür; kullanıcı tarafında sessiz sakin bir devamlılık hissi yaratır.
CDN üzerinde başka bir tatlı numara da, köke giden istek sayısını “makul” tutmak. Eğer bir anda onlarca edge konumundan aynı anda tazeleme isteği patlıyorsa, origin gereksiz strese girer. Bu yüzden kısa max-age ve bir tık daha uzun stale-while-revalidate çoğu zaman ideal dengedir. Edge tek tek, nazikçe tazeler.
WordPress’te Dayanıklı Cache: Eklenti mi, functions.php mi, Sunucu mu?
Basit ama etkili bir oyun planı
WordPress dünyasında sayfa önbelleği genelde bir eklentiyle çözülüyor. Ama iş sadece “cache var mı?” sorusu değil. Asıl farkı, nasıl cache’lediğiniz ve ne zaman bayat içeriği göstermeyi kabul ettiğiniz yaratıyor. Burada üç katman var: Uygulamanın kendisi, web sunucusu (Nginx/Apache) ve CDN (Cloudflare). En temiz kurguda, WordPress uygun başlıkları üretir, Nginx bunları geçirir, Cloudflare de uygular. Zincir böyle akınca işler tatlı.
Örneğin functions.php veya küçük bir “mu-plugin” ile sadece ziyaretçi sayfalarında basit bir başlık ekleyebilirsiniz. Admin veya giriş yapmış kullanıcıyı ayırmak önemli. Bunu çok yormadan şöyle yapıyoruz:
// wp-content/mu-plugins/cache-headers.php
add_action('send_headers', function () {
if (is_user_logged_in()) {
header('Cache-Control: private, no-store, max-age=0');
return;
}
if (is_admin() || is_search()) {
header('Cache-Control: private, no-store, max-age=0');
return;
}
// Ana sayfa, kategori, yazı gibi herkese açık sayfalar
header('Cache-Control: public, max-age=60, stale-while-revalidate=300, stale-if-error=86400');
});
Bu sayede WordPress tarafında “niyeti” belli etmiş olursunuz. Nginx ve Cloudflare da bu niyete saygı duyar. Elbette WooCommerce gibi sepet, ödeme, hesap sayfalarında bu başlıkları göndermemek gerekir. Çoğu popüler cache eklentisi zaten bu sayfaları otomatik ayırıyor; ama emin olmak adına header mantığını kendi gözünüzle basitçe kontrol etmek iyi bir refleks.
Bu noktada altyapı dayanıklılığını da hatırlatmadan geçmeyeyim. Örneğin trafik piklerinde TCP ayarlarını doğru yapmak, arka planın sakin kalmasına yardımcı olur. Detaylı pratikler için şu notlar epey iş görür: yüksek trafikli WordPress’te Linux TCP tuning ipuçları. Dayanıklı cache tek başına kahraman değildir; doğru ağ ayarlarıyla kol kola yürür.
Gerçek Dünya Senaryosu: Bakım Anı, Sıcak Deploy ve Eski Dost “Stale”
Beş dakikalık fırtınada şemsiyeyi açmak
Bir müşteride yaşadığım kısa ama öğretici bir akşamı anlatayım. Staging’den prod’a küçük bir özellik geçecektik. Veritabanında birkaç migration, backend’de ufak bir API değişikliği. “Çok sürmez” dedik ama yaşam bu, iki dakikalık iş beş dakikaya uzadı. Tam o sırada mobil trafik yükseldi. Korkulan senaryo geldi: backend bazı endpoint’lerde 502 verdi.
Neyse ki önceden hazırlığımız vardı. Ürün listeleme ve blog sayfalarında public, max-age=60, stale-while-revalidate=300, stale-if-error=3600 vardı. Cloudflare da origin’in dediklerini dinleyecek şekilde ayarlanmıştı. Kullanıcılar ne gördü? Eski ama düzgün sayfalar. Arada bir iki tıklama gecikti, ama kimse “Site çöktü” demedi. Biz de arkada sakin sakin deploy’u tamamladık.
Benzer akışlarda load balancer kullananlar için de küçük bir öneri: L4/L7 seviyesinde akışı şıkça yönetmek, çok kısa hataları kullanıcıdan saklamakta yardımcı olur. Mesela HAProxy ile sıfır kesinti sunan bir yük dengeleme düzeni kurulduğunda, stale stratejisiyle çok iyi anlaşır. Bir balık, bir ekmek gibi.
Planlı bakım anlarında DNS ve dağıtım düzeninizi otomatik hale getirmek de işe yarar. Örneğin canary dağıtımda tek bir node’u güncellersiniz, sorun yoksa diğerini. Bu kurguyu pratikleştirmek için Terraform ile VPS ve DNS otomasyonu üzerine yazdığım notlar, günlük hayatta defalarca hayat kurtardı. Cache sabır verir, otomasyon hız kazandırır.
Doğru Süreleri Seçmek: Fazla Bayat, Az Taze Dengesini Kurmak
Ne zaman kısa, ne zaman uzun?
İşin püf noktası şu: max-age’i çok kısa tutarsanız, arka planda sürekli tazeleme olur; origin’e yük artar. Çok uzun tutarsanız, değişiklikler geç görünür. Benim mutfakta kullandığım basit tarif: “Kritik ama nispeten durağan sayfalarda 30-120 saniye max-age, üzerine 5-10 dakika stale-while-revalidate; hata anında 15-60 dakika stale-if-error.” Elbette bu bir şablon, sizin trafiğiniz ve güncelleme hızınız esas belirleyicidir.
Mesela “yeni gelen ürünler” sayfası gündüzleri daha sık değişiyorsa, gündüz sürelerini kısa, geceleri bir tık uzun tutabilirsiniz. Bunu otomatikleştirmek isteyenler için, uygulama tarafında saat bazlı küçük bir varyasyon yazmak hiç zor değil. Üstelik CDN tarafında “cache key”i bozmadan, sadece başlık üzerinden yönetirsiniz.
Özetle, stale-while-revalidate ile kullanıcı hız hissini kaybetmesin, stale-if-error ile küçük hatalar görünmesin. Bununla birlikte “gerçek zamanlılık” ihtiyacı olan alanlarda (sepetteki adet, kişisel bildirimler, canlı skor gibi) cache’i dikkatle sınırlandırmak şart. Hepsi aynı tencerede pişmez.
Gözlem ve Sorun Giderme: “Cache Çalışıyor mu?” Diyen İçin Küçük Araç Çantası
Header’lara bak, nefes al, sonra karar ver
“Peki nasıl anlayacağız?” sorusu sık gelir. Ben ilk olarak basitçe curl -I ile başlarım. Yanıt başlıklarında Cache-Control, Age, CF-Cache-Status veya X-Cache-Status gibi ipuçlarına bakarım. Nginx tarafında $upstream_cache_status ile “MISS, HIT, EXPIRED, STALE” gibi değerleri log’a düşmek neyin olup bittiğini anlamayı kolaylaştırır.
curl -I https://ornek.com/
HTTP/2 200
cache-control: public, max-age=60, stale-while-revalidate=300, stale-if-error=86400
age: 42
x-cache-status: HIT
Tarayıcı geliştirici araçlarındaki “Network” paneli de yardımcıdır. Bir isteği tekrar yollayın, “from disk cache / from memory cache” veya “revalidated” gibi notlara bakın. Cloudflare kullanıyorsanız, CF-Cache-Status: HIT başarılı bir edge cache’i, MISS veya EXPIRED tazelemeyi, STALE ise bayat içeriğin servis edildiğini gösterir. Bu küçük ipuçlarıyla, “Neden yavaşladı?” sorusunu çok daha hızlı yakalarsınız.
Hazır yeri gelmişken şu ufak notu da düşeyim: Edge ve origin TLS ayarlarınız düzgün olursa, iletişim pürüzsüz akar. Meraklısına, Nginx/Apache’de ECDSA + RSA ikili SSL üzerine notlar da pratikte hissedilir fark yaratır. Her katmanı tıkır tıkır yaptığınızda, cache stratejisi çok daha etkili sonuç verir.
Gelişmiş İpuçları: İnce Ayarlarla Tatlandırmak
Vary, ETag ve arkadaşları
Gün gelir “Dil seçimi, cihaz tipi” gibi farklı varyasyonları yönetmek istersiniz. Burada Vary başlığı devreye girer. Örneğin Vary: Accept-Encoding zaten klasik. Buna ilave olarak Vary: Accept-Language kullanıyorsanız, cache’iniz dil başına ayrı kopya tutar. Bu iyi mi kötü mü? İçeriğinize bağlı. Eğer tek dildeyseniz, vary’ı abartmayın. Gereksiz vary çoğalması, cache etkinliğini düşürür.
ETag ve If-None-Match da tazeleme anlarında bant genişliğini korumak için yararlıdır. ETag sabit kaldıkça, “304 Not Modified” döner ve yük hafifler. Ama şunu unutmayın: stale-while-revalidate ile hedeflenen şey, hızlıca bir şey göstermek. ETag ise daha çok “değişmedi, boşuna indirme” demek. İkisi birbirini tamamlar ama aynı rolü oynamaz.
Cloudflare veya Nginx tarafında, cache anahtarınızı aşırı zenginleştirmekten kaçının. Query string’in her bir parametresi anahtara girerse, “tek sayfa on farklı anahtar”a dönüşür. Bunun yerine kritik parametreleri tutup, geri kalanını yoksaymak çoğu zaman daha verimli. Tam tersi, kişiselleştirme varsa, o parametreyi mutlaka anahtara dahil edin. İnce ayar dediğimiz bu iş, birkaç gün gözlemle kendini belli eder.
Bir dipnot daha: CDN ve origin güncellemelerini aynı anda koşarken “boşluk” anı yaratmamaya çalışın. Örneğin DNS, TLS, backend ve cache ayarlarınızın birlikte akması için VPS ve DNS otomasyonu yazısındaki gibi küçük otomasyonlar harika iş görür. Bir de, kesinti dayanımını artırmak için ayakta kalan node’ları saygıyla yönetmek gerekir; burada HAProxy gibi bir katman, kötü sürprizlere karşı iyi bir yastık olur.
Sık Karşılaşılan Hatalar: Neleri Yapmayalım?
“Her şeyi cache’e koy” değil, “doğru şeyi doğru süreyle”
İlk hata genelde şu: Her şeyi cache’e koyup, sonra “aa kullanıcı X, Y’nin sepetini gördü” gibi tatsız sürprizlerle uğraşmak. Net ayrım şart. Giriş yapılan sayfalar, ödeme adımları, kullanıcıya özel raporlar no-store olsun. Geri kalan herkese açık listeler, blog yazıları, kategori sayfaları public ve stale’li.
İkincisi, kök sayfayı çok uzun süre taze tutmak. “Anasayfa sabit ya” diyerek 1 saat taze tutunca, içerik ekleyen ekip “niye görünmüyor” der. Bu moral bozucu olur. Onun yerine kısa taze, uzun stale iyi gider. Üçüncüsü, debug etmeyi zorlaştırmak. Lütfen hem Nginx hem CDN tarafında basit bir “X-Cache-Status” benzeri header döndürün. Sorun çözmek, gözle görünür olduğunda kolaydır.
Son olarak, izleme olmadan cache stratejisi yürütmeyin. Hata oranlarını, yanıt sürelerini, edge miss/hit dağılımını izleyin. Log’lara ara sıra bakın. Bir iki küçük grafik, koca fırtınaları önceden gösterir. Bu arada Cloudflare ve benzeri CDN’lerde Cache-Control’ün davranışları için üretici dokümantasyonları da günceldir; isterseniz bir ara Cloudflare’ın cache-control notlarına göz atın.
Kapanış: Küçük Bir Başlık, Büyük Bir Huzur
Yolun sonu değil, güzel bir başlangıç
stale-while-revalidate ve stale-if-error, kulağa basit geliyor. Aslında basitler de. Güçleri, doğru yerde, doğru süreyle devreye girmelerinden geliyor. Nginx tarafında iki üç satır ayar, WordPress’te bir küçük mu-plugin, Cloudflare’de “origin’in dediğini dinle” prensibi… Hepsi birleşince kullanıcı tarafında pürüzsüz bir deneyim doğuyor. Kesintiler hayatın gerçeği, ama herkesin gözü önünde olması şart değil.
Bu yazıda önce mantığını konuştuk, sonra sahada nasıl kuracağımızı gösterdik. Artık elinizde pratik bir çanta var: HTTP başlıklarını anlayan göz, Nginx’te arka plan güncellemeyi açan parmak, Cloudflare’de sakinlik veren bir kural seti. Bundan sonrası, küçük ölçüp biçmeler. İlk adımda anasayfa ve blog listesi gibi sayfalarda başlayın. Sonra trafiğinize göre süreleri oynayın. Gözlemleyin, ince ayar verin. Küçük dokunuşlarla büyük rahatlık geliyor.
Umarım bu notlar işinize yarar. Sizin de sahadan hikâyeleriniz varsa, duymayı çok isterim. Bir dahaki yazıda yine altyapının mutfağından bir başka tatlı stratejiyle buluşalım. O zamana kadar, cache’iniz hep “sakin”, kullanıcılarınız hep “mutlu” kalsın.
