Sitede bir güncelleme yaptın, kendi tarayıcında her şey yolunda görünüyor ama müşteri "bende hâlâ eski hâli duruyor" diyor. Ya da tam tersi: her sayfa geçişinde aynı logo, aynı font ve aynı stil dosyası baştan indiriliyor. Bu iki durumun da açıklaması tarayıcı önbelleğinde saklı. Tarayıcı önbelleği, kullanıcının cihazında yaşayan ve senin doğrudan erişemediğin bir depodur; onu yalnızca gönderdiğin HTTP başlıklarıyla yönlendirebilirsin.
Bu rehberde tarayıcıda kaç ayrı önbellek katmanı olduğunu, bir isteğin hangi sırayla karara bağlandığını, bellek ile disk önbelleği arasındaki farkı, geri/ileri tuşlarını anında çalıştıran bfcache mekanizmasını, güncellemelerin kullanıcıya ulaşması için kullanılan cache busting yöntemini ve gerçek davranışı geliştirici araçlarında nasıl doğru okuyacağını anlatacağım.
Tarayıcıda Kaç Farklı Önbellek Var#
"Tarayıcı önbelleği" tek bir depo değildir; birbirinden bağımsız çalışan birkaç katmandır ve hangisinin devreye girdiğini bilmezsen teşhis yapamazsın.
| Katman | Nerede yaşar | Kim kontrol eder | Sayfa kapanınca |
|---|---|---|---|
| Bellek önbelleği | RAM | Tarayıcı | Kaybolur |
| Disk önbelleği (HTTP cache) | Disk | HTTP başlıkların | Kalır |
| Service Worker Cache | Disk | Senin JavaScript kodun | Kalır |
| Bfcache | RAM | Tarayıcı | Kaybolur |
| Ön yükleme önbelleği | RAM | Sayfa ipuçların | Kaybolur |
Disk önbelleği, klasik anlamda "tarayıcı önbelleği" dediğimiz şeydir ve Cache-Control, ETag, Last-Modified başlıklarıyla yönetilir. Bu yazının ana konusu odur. Başlıkların anlamlarını Cache-Control başlıkları rehberinde tek tek açıkladım.
Bellek önbelleği, aynı sekmede kısa süre içinde tekrar istenen kaynaklar için kullanılan geçici bir kopyadır. Aynı görselin bir sayfada üç yerde geçmesi hâlinde tarayıcı onu bir kez indirir. Sekme kapandığında yok olur.
Service Worker Cache, tamamen senin kodunun yönettiği ayrı bir depodur. HTTP başlıklarını dinlemez; hangi yanıtın saklanacağına ve hangisinin sunulacağına yazdığın JavaScript karar verir. Bu yüzden bir güncelleme sorunu yaşıyorsan ve sitende service worker varsa, HTTP başlıklarını değiştirmek tek başına yeterli olmayabilir.
Bfcache ise sayfanın çalışır hâldeki tam anlık görüntüsüdür ve ayrı bir bölümü hak ediyor.
Bir İsteğin Kararı Nasıl Verilir#
Tarayıcı bir kaynağı istediğinde şu sırayla ilerler:
- Service worker var mı? Varsa istek önce ona uğrar. Kodun bir yanıt döndürürse iş orada biter.
- Bellek önbelleğinde taze kopya var mı? Varsa doğrudan kullanılır, ağa hiç çıkılmaz.
- Disk önbelleğinde kopya var mı? Yoksa istek ağa çıkar.
- Kopya taze mi?
max-agesüresi dolmamışsa doğrudan kullanılır. Ağ trafiği sıfırdır. - Kopya bayatsa doğrulanabilir mi?
ETagveyaLast-Modifiedvarsa koşullu istek yapılır; sunucu304dönerse eldeki kopya kullanılır. - Hiçbiri olmazsa kaynak tam olarak indirilir.
Bu akışın hiç başlamadığı durumlar da var ve bunları bilmek çok zaman kazandırır. Yanıt no-store ile geldiyse saklanacak bir kopya hiç oluşmaz. POST gibi durum değiştiren isteklerin yanıtları normal şartlarda önbelleğe alınmaz. Authorization başlığı taşıyan istekler, yanıtta açıkça public yazmadıkça paylaşımlı önbelleklerde saklanmaz. Ve sayfa güvenli bir bağlantı üzerinden gelmiyorsa service worker hiç çalışmaz. Beklediğin önbellek davranışını göremiyorsan, başlıkları satır satır incelemeye başlamadan önce bu dört koşuldan birine takılıp takılmadığını kontrol et.
Dördüncü ve beşinci adım arasındaki fark performans açısından kritiktir. Taze bir kopya ağa hiç çıkmaz; bayat ama doğrulanabilir bir kopya ise gidiş-dönüş süresini yine öder, sadece gövdeyi indirmez. Yani 304 iyidir ama "hiç istek yapmamak" çok daha iyidir. Doğrulama mekaniğinin ayrıntısını ETag ve Last-Modified farkı yazısında ele aldım.
Modern tarayıcılarda bilmen gereken bir kısıt daha var: önbellek bölümlenmiştir. Eskiden iki farklı site aynı CDN'den aynı kütüphaneyi çekiyorsa, ikincisi birincinin önbelleğinden faydalanırdı. Gizlilik nedeniyle bu davranış kaldırıldı; önbellek artık ziyaret edilen üst düzey siteye göre ayrılır. Pratik sonucu şudur: "popüler bir CDN'den ortak kütüphane çekersem kullanıcıda zaten vardır" varsayımı artık geçerli değildir. Kendi varlıklarını kendi alan adından sunmak çoğu durumda daha iyidir.
Bellek, Disk ve Bfcache#
Geliştirici araçlarındaki Network sekmesinde "Size" sütununa baktığında birkaç farklı ifade görürsün ve her biri farklı bir katmanı gösterir:
(memory cache) -> RAM'den geldi, ag trafigi yok
(disk cache) -> Diskten geldi, ag trafigi yok
(ServiceWorker) -> Service worker'in yanitladi
304 -> Dogrulandi, govde indirilmedi
200 + boyut -> Tam olarak indirildi
(memory cache) görüyorsan kaynak o oturumda zaten indirilmiştir. (disk cache) ise kalıcı depodan geldiği anlamına gelir ve asıl hedefin budur.
Bfcache (back/forward cache), diğerlerinden tamamen farklı çalışır. Kullanıcı başka bir sayfaya geçtiğinde tarayıcı, terk edilen sayfanın tam durumunu, çalışan JavaScript belleği dahil, bellekte tutar. Geri tuşuna basıldığında sayfa yeniden yüklenmez; dondurulduğu yerden devam eder. Sonuç neredeyse anlık bir geri dönüştür ve mobil deneyimde çok belirgindir.
Bfcache'in çalışmasını engelleyen birkaç durum var ve bunlardan biri fazlasıyla yaygın bir yapılandırma hatası: ana belgeye Cache-Control: no-store göndermek. Bu direktif, sayfanın hiçbir biçimde saklanmaması gerektiğini söylediği için bfcache de devre dışı kalır ve geri tuşu her seferinde tam bir yeniden yükleme yapar. Kişiye özel HTML'de no-store yerine private, max-age=0, must-revalidate kullanmak, hem gizliliği korur hem bfcache'i açık bırakır. Gerçekten hassas sayfalarda (ödeme adımı, banka bilgisi) no-store doğru tercihtir; ama sitenin tamamına uygulanması kayıptır.
Eski unload olay dinleyicileri de bfcache'i engeller. Bunun yerine pagehide ve visibilitychange olaylarını kullanmalısın; işlevsel olarak aynı ihtiyacı karşılarlar ve bfcache uyumludurlar.
Cache Busting: Dosya Adına Sürüm Koymak#
Uzun önbellek süresi vermenin tek gerçek riski, güncellemelerin kullanıcıya ulaşmamasıdır. Bu riski ortadan kaldıran yöntem basittir: dosyanın adını içerikle birlikte değiştir.
Kötü: /assets/app.css -> guncelleme yayilmaz
Orta: /assets/app.css?v=12 -> bazi ara katmanlar sorgu dizesini yok sayar
İyi: /assets/app.a1b2c3d4.css -> icerik degisince ad degisir
Üçüncü yöntemde dosya adı içerikten türetilmiş bir hash taşır. İçerik değiştiğinde ad da değişir, tarayıcı bunu tamamen yeni bir kaynak olarak görür ve indirir. Eski adres hâlâ eski içeriği gösterir; kimseye zararı yoktur ve bir süre sonra kendiliğinden düşer. Bu yaklaşımın en güzel yanı, artık hiçbir önbelleği temizlemene gerek kalmamasıdır.
Sunucu tarafında bu dosyalara güvenle en uzun ömrü verebilirsin:
# Adinda icerik hash'i olan varliklar: bir yil, dogrulama bile yok
location ~* "\.[a-f0-9]{8,}\.(js|css|woff2)$" {
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
# HTML: kisa omurlu, cunku yeni dosya adlarini o tasiyor
location / {
add_header Cache-Control "public, max-age=60, must-revalidate";
}
Buradaki denge önemlidir: varlıklar sonsuza kadar önbelleklenir, HTML ise kısa ömürlüdür. Çünkü yeni varlık adlarını kullanıcıya taşıyan şey HTML'dir. HTML güncellendiği an yeni adlar gelir ve doğru dosyalar indirilir.
Modern derleme araçları bu adlandırmayı zaten kendiliğinden yapar. Elle yönetilen bir sitede ise en azından sorgu dizesi yöntemini kullan; hash'li ad kadar sağlam değildir ama hiç yoktan iyidir. CDN kullanıyorsan bu yaklaşımın uç katmandaki karşılığını CDN nedir, nasıl çalışır yazısında bulabilirsin.
Yenileme Türleri ve Doğru Test Etme#
Test ederken en sık yapılan hata, tarayıcının farklı yenileme türlerinin farklı davrandığını bilmemektir.
| Eylem | Davranış | Ne zaman kullan |
|---|---|---|
| Bağlantıya tıklama | Normal önbellek akışı | Gerçek kullanıcı deneyimini görmek |
| Adres çubuğunda Enter | Normal önbellek akışı | Aynı |
| Yenile (F5) | Ana belge doğrulanır, varlıklar genelde önbellekten | Gündelik kontrol |
| Sert yenileme (Ctrl+Shift+R) | Her şey önbellek atlanarak indirilir | Yeni sürümü zorla görmek |
| Geliştirici araçları açık + "Disable cache" | Önbellek tamamen devre dışı | Ağ boyutlarını ölçmek |
Buradan çıkan pratik kural şudur: "bende düzeldi" demeden önce sert yenileme yapmadığından emin ol. Sert yenileme yapıp sorunu görmemek, sorunun çözüldüğü anlamına gelmez; sadece o testte önbelleği atladığın anlamına gelir. Gerçek davranışı görmek için normal gezinme yapmalısın.
Komut satırından yapılan test her zaman daha güvenilirdir, çünkü hiçbir yerel önbellek araya girmez:
# Sunucu gercekte ne gonderiyor
curl -sSI https://firmaniz.com/assets/app.a1b2c3d4.css \
| grep -iE 'cache-control|etag|last-modified|age'
# Kosullu istek gercekten 304 donuyor mu
curl -sS -o /dev/null -w '%{http_code}\n' \
-H 'If-None-Match: "a1b2c3d4"' \
https://firmaniz.com/assets/app.a1b2c3d4.css
Kullanıcıda inatçı bir önbellek sorunu varsa ve nedenini bulamıyorsan, sunucudan Clear-Site-Data başlığı göndererek tarayıcının depolarını temizletebilirsin. Bu ağır bir müdahaledir; oturumları ve yerel verileri de silebileceği için yalnızca gerçekten gerektiğinde ve hedefli biçimde kullan.
Sık Yapılan Hatalar#
En yaygın hata, sitenin tamamına no-store vermektir. Bu, hem varlıkların hiç önbelleklenmemesine hem de bfcache'in devre dışı kalmasına yol açar; geri tuşu yavaşlar, her ziyaret tam indirme olur. no-store yalnızca gerçekten hassas yanıtlar içindir.
İkinci hata, adı sabit dosyalara çok uzun max-age vermektir. style.css dosyasına bir yıl verdiysen, yaptığın değişiklik mevcut ziyaretçilerin çoğuna hiç ulaşmaz ve elinde bunu düzeltecek bir mekanizma yoktur. Uzun süre vermenin ön koşulu, adın içerikle birlikte değişmesidir.
Üçüncü hata, HTML'e uzun ömür vermektir. HTML, yeni varlık adlarını taşıyan belgedir; o bayat kalırsa yeni dosyalardan kimsenin haberi olmaz. HTML'i her zaman kısa ömürlü tut.
Dördüncü hata, service worker'ın varlığını unutmaktır. Sitende bir service worker kayıtlıysa istekleri o karşılıyor olabilir ve HTTP başlıklarını değiştirmen hiçbir şeyi değiştirmez. Geliştirici araçlarının Application sekmesinden kayıtlı worker'ı kontrol et.
Beşinci hata, önbelleğin garanti olduğunu sanmaktır. Tarayıcı disk baskısı altında önbelleği kendi inisiyatifiyle boşaltabilir; gizli pencerede önbellek zaten pencere kapanınca yok olur. Önbellek bir optimizasyondur, bir depolama garantisi değildir. Uygulamanın doğruluğu hiçbir zaman önbelleğin varlığına bağlı olmamalıdır.
Sıkça Sorulan Sorular#
Tarayıcı önbelleğini kullanıcı adına temizleyebilir miyim#
Doğrudan hayır; kullanıcının cihazındaki depoya erişimin yoktur. Yapabileceğin iki şey var: dosya adını değiştirerek eski kopyayı ilgisiz hâle getirmek, ya da sunucudan Clear-Site-Data başlığı göndererek tarayıcıya temizleme talimatı vermek. İlki her zaman tercih edilmelidir; ikincisi oturum ve yerel verileri de etkileyebildiği için son çare olarak kullanılır.
Sitem güncellendi ama kullanıcılar eski hâlini görüyor, ne yapmalıyım#
Önce HTML'in önbellek süresini kontrol et; uzun bir max-age verdiysen kullanıcı eski HTML'i görüyordur ve o da eski varlık adlarını gösterir. Ardından varlık dosyalarının adında içerik hash'i olup olmadığına bak. Kalıcı çözüm, HTML'i kısa ömürlü tutup varlık adlarını içerikle birlikte değiştirmektir; bu yapıldığında sorun bir daha tekrarlamaz.
Bellek önbelleği ile disk önbelleği arasındaki fark nedir#
Bellek önbelleği RAM'de yaşar, çok hızlıdır ama sekme kapandığında kaybolur; aynı sayfada tekrar tekrar istenen kaynaklar için kullanılır. Disk önbelleği kalıcıdır, tarayıcı kapansa bile durur ve gönderdiğin HTTP başlıklarıyla yönetilir. Tekrar ziyaretlerde hız kazandıran katman disk önbelleğidir, dolayısıyla başlıklarını ona göre ayarlarsın.
Bfcache neden çalışmıyor#
En sık sebep, ana belgeye Cache-Control: no-store gönderilmesidir; bu direktif sayfanın saklanmasını tümüyle yasakladığı için bfcache de devre dışı kalır. İkinci sık sebep, sayfada unload olay dinleyicisi bulunmasıdır. Çözüm, gerçekten hassas olmayan sayfalarda no-store yerine private, max-age=0, must-revalidate kullanmak ve unload yerine pagehide olayına geçmektir.
Sert yenileme ile normal yenileme arasındaki fark nedir#
Normal yenilemede tarayıcı ana belgeyi sunucuya doğrulatır ama varlıkları büyük ölçüde önbellekten kullanır. Sert yenilemede ise önbellek tamamen atlanır ve her kaynak baştan indirilir. Bu yüzden sert yenileme, kullanıcının gerçekte yaşadığı deneyimi göstermez; test ederken normal gezinme yapmalı ya da doğrudan komut satırından ölçüm almalısın.
Ortak CDN'den kütüphane çekmek hâlâ avantajlı mı#
Eskiden avantajlıydı çünkü aynı dosya başka sitelerde de kullanılıyorsa kullanıcının önbelleğinde hazır bulunabiliyordu. Modern tarayıcılar gizlilik nedeniyle önbelleği ziyaret edilen siteye göre ayırdığı için bu paylaşım artık gerçekleşmiyor. Bugün kütüphaneleri kendi alan adından sunmak genellikle daha iyidir: ekstra bağlantı kurulumu ödemezsin ve dosyanın sürümünü tamamen sen kontrol edersin.
Kapanış#
Tarayıcı önbelleği, kullanıcının cihazında yaşayan ve doğrudan erişemediğin bir depodur; onu yalnızca gönderdiğin başlıklarla yönetirsin. Aklında tutman gereken dört alışkanlık şu: HTML'i her zaman kısa ömürlü tut, varlıklara uzun ömür vereceksen adlarına içerik hash'i koy, no-store'u yalnızca gerçekten hassas yanıtlarda kullan ki bfcache açık kalsın, ve testi sert yenilemeyle değil normal gezinmeyle ya da komut satırından yap.
Bu başlıkları sunucu tarafında hayata geçirmek istersen paylaşımlı hosting için htaccess önbellek ve sıkıştırma, kendi sunucun için Nginx FastCGI cache yazımız yol gösterir. Hızlı bir zemin arıyorsan web hosting ve WordPress hosting paketlerimize, tam kontrol ve kendi önbellek yapılandırman için VDS sunucularımıza göz atabilirsin. Ayarları bize bırakmak istersen sunucu yönetimi hizmetimiz bu yapılandırmayı senin yerine kurar ve izler.