Tarayıcı Önbellekleme Nasıl Yapılır?
Tarayıcı önbellekleme denince çoğu ekip doğrudan “dosyaları daha uzun saklayalım” diye düşünür. Sorun tam burada başlıyor. Doğru kurulduğunda önbellek hem tekrar ziyareti hızlandırır hem de sunucunun aynı dosyayı durmadan taşımasını engeller; yanlış kurulduğunda ise eski CSS, eski JavaScript ve dağılmış deployment davranışı üretir. Bu yüzden asıl iş yalnızca hız vermek değil, hangi dosyanın ne kadar süre yaşayacağını dürüstçe ayırmaktır.
Tipik senaryo basit: menüde küçük bir CSS düzeltmesi yaparsınız, deployment biter, siz yeni sürümü görürsünüz; ama kullanıcıların bir kısmı eski arayüzle devam eder. Birinde buton kaymış, diğerinde eski script çalışıyor, bir başkasında ikonlar yerli yerinde değil. Aynı kod tabanı, aynı domain, farklı sonuçlar. Çoğu zaman problem uygulamada değil, önbellek stratejisinde çıkar.
Tarayıcı önbelleği bir hız oyuncağı değil. Teslimat sözleşmesi gibi düşünmek daha doğru. Tarayıcıya, “bu dosyayı ne kadar süre güvenle saklayabilirsin, ne zaman yeniden soracaksın, ne zaman hiç düşünmeden kullanacaksın?” diyorsunuz. Bu karar dosya türüne göre değişir. HTML, hash'li CSS, hash'siz görsel, font, üçüncü taraf script; hepsi aynı kuralla yönetilirse kısa vadede basit görünür, orta vadede baş ağrıtır.
Bir de şu var: önbellek yalnızca tekrar ziyaret meselesi değildir.
Kullanıcı aynı oturum içinde üç yazı geziyorsa, her sayfada aynı CSS ve aynı temel JavaScript dosyalarının yeniden taşınmaması ciddi fark yaratır. Özellikle içerik sitelerinde bu fark sessiz çalışır; kimse “önbellek harikaydı” demez ama site daha derli toplu, daha hazır ve daha güvenilir hissedilir.
Sorun genelde nerede başlıyor?
En sık hata, HTML ile statik varlıkları aynı kuralla düşünmek. Oysa görevleri farklı.
HTML, sayfanın hangi CSS ve JavaScript dosyalarını çağıracağını belirler. Yeni sürümde dosya adı değiştiyse kullanıcı önce yeni HTML'i almalı ki yeni varlıklara yönlensin. HTML'i saatlerce ya da günlerce agresif biçimde önbellekte tutarsanız, deployment bitmiş olsa bile ziyaretçi eski referanslarla dolaşabilir. Buna karşılık dosya adı sürümlenmiş, örneğin app.91f3c2.js gibi hash taşıyan statik varlıkları çok daha uzun süre saklamak güvenlidir; çünkü yeni sürüm geldiğinde isim zaten değişir.
Yani burada tek doğru yok; dosyanın rolüne göre doğru var.
Basit görünen ama sağlam çalışan ayrım şu: HTML yeniden doğrulansın, sürümlenmiş statik dosya uzun yaşasın, sürümlenmemiş statik dosya daha temkinli yönetilsin.
Tarayıcı önbelleği tam olarak ne yapar?
Tarayıcı, daha önce indirdiği bir kaynağı tekrar ihtiyaç duyduğunda önce sunucuya gitmek zorunda kalmayabilir. Uygun başlıklar varsa elindeki dosyayı doğrudan kullanır. Bazen dosyayı tamamen yeniden kullanır, bazen “bu hâlâ geçerli mi?” diye yeniden doğrular, bazen de hiç saklamaz. Farkı belirleyen şey sizin verdiğiniz başlıklardır.
Burada en sık karıştırılan kavram şu: önbellek yalnızca “sakla ya da saklama” kararı değildir. Aynı zamanda “sakla ama her seferinde yeniden sor”, “şu süre boyunca hiç sorma”, “bu dosya değişmediyse 304 ile geç”, “bu dosya sürümlü olduğu için güvenle kullan” gibi daha ince davranışlar da vardır. Bu farkı anlamadan yapılan ayar, genellikle ya aşırı agresif olur ya da gereğinden fazla çekingen kalır.
Özellikle PageSpeed Insights raporunda “serve static assets with an efficient cache policy” benzeri uyarıları gören ekipler bazen refleksle tüm dosyalara uzun max-age basar. Rapor puanı biraz toparlanır, sonra deployment tarafı dağılır. Ölçüm sinyali tek başına strateji kurmaz; dosya yaşam döngüsünü de düşünmek gerekir.
no-cache, no-store, max-age ve immutable ne işe yarar?
Bu başlıkları karıştırmak çok kolay. Bir kez netleştirelim.
no-cache, dosyayı hiç saklama demek değildir. Tarayıcı saklayabilir; ama kullanmadan önce sunucuya yeniden sorar. HTML için çoğu projede dengeli çözüm budur. Kullanıcı geri geldiğinde tarayıcı elindeki kopyayı körlemesine kullanmaz, tekrar doğrular.
no-store ise daha serttir. Kaynağı kaydetme, mümkünse hiçbir yerde tutma anlamına gelir. Kimlik doğrulama ekranı, çok hassas veri, tek kullanımlık yanıtlar gibi özel durumlarda mantıklıdır. Ama sıradan içerik sayfalarında çoğu zaman gereğinden sert davranır. Üstelik geri/ileri gezinme davranışını ve tarayıcı optimizasyonlarını gereksiz kısıtlayabilir. Burada dikkatli olun.
max-age, kaynağın kaç saniye boyunca taze kabul edileceğini söyler. Örneğin max-age=31536000 bir yıl demektir. Hash'li statik dosya için gayet mantıklıdır. Aynı süreyi sabit isimli site.css dosyasına uygularsanız, yeni sürüm yayımlandığında kullanıcı uzun süre eski dosyayı tutabilir.
immutable ise “bu kaynak taze kaldığı sürece yeniden doğrulamaya çalışma” demektir. Yalnızca sürümlenmiş varlıklarda gerçekten anlamlıdır. Dosya adı değişmeden dosya içeriği değişebiliyorsa bu başlık tehlikeli hale gelir.
HTML için çoğu durumda yeniden doğrulama mantığı gerekir. Hash'li CSS/JS için uzun ömürlü ve rahat politika uygundur. Sabit isimli dosyalarda ise daha kısa yaşam veya zorunlu sürümleme gerekir.
Güvenli strateji nasıl kurulur?
Benzer projelerde en güvenli yaklaşım şu sırayla ilerler:
- Önce hangi dosyaların sürümlendiğini ayırın.
- HTML'i statik varlıklardan ayrı yönetin.
- Hash'li dosyalara uzun ömürlü politika verin.
- Hash'siz dosyalarda daha kısa süre veya daha net yeniden doğrulama kullanın.
- Deployment sonrası başlıkları gerçekten kontrol edin.
Teorik doğruluk yetmez; deployment davranışıyla eşleşme gerekir. Build sistemi dosya adı sürümlüyorsa tarayıcıya daha cesur başlık verebilirsiniz. Yapmıyorsa yalnızca sunucu katmanında uzun önbellek süresi basmak iyi fikir değil.
İçerik siteleri için pratikte işe yarayan temel dağılım: HTML tarafında Cache-Control: no-cache, must-revalidate veya buna yakın bir politika. Hash'li CSS ve JavaScript tarafında public, max-age=31536000, immutable. Hash'siz ama sık değişmeyen görsellerde daha temkinli bir süre; örneğin 7 gün, 30 gün veya proje ritmine göre benzeri. Font tarafında da yine sürümleme varsa uzun ömürlü yaklaşım mantıklı.
ETag ve Last-Modified ne kadar yeterli?
Bu iki başlık çoğu zaman konuşmaya geç girer, ama önemli. Çünkü önbellek yalnızca “dosyayı sakla” kararı değildir; bazen “sakla, sonra bana tekrar sor” davranışıdır. ETag veya Last-Modified devredeyse tarayıcı kaynağı tamamen yeniden indirmek yerine sunucuya doğrulama isteği atar. Dosya değişmemişse 304 döner ve gövde tekrar taşınmaz. Bu özellikle HTML veya kısa ömürlü kaynaklarda anlamlıdır.
Yine de burada sınırı doğru çizmek gerekir. Doğrulama, sürümlemenin yerine geçmez. Tarayıcı 304 ile daha az veri taşısa bile yine ağa gider, yine bekler. Hash'li dosya için hedef çoğu zaman her istekte yeniden sormak değil, bir süre hiç sormadan güvenle kullanmaktır. O yüzden sürümlenmiş statik dosyalarda asıl kazanım ETag değil, doğru Cache-Control ve dosya adı stratejisidir.
Buna karşılık HTML tarafında doğrulama mantığı değerlidir. Çünkü HTML'in görevi güncel varlıkları işaretlemektir. Her deployment sonrası yeni HTML'i daha erken görmeniz gerekir. Burada 304 davranışı çoğu zaman mantıklıdır; her istekte tüm gövdeyi taşımak yerine, değişmediyse küçük bir doğrulama ile geçersiniz. Özellikle içerik sitelerinde bu denge gayet iş görür.
Kısacası ETag ve Last-Modified yararsız değil. Ama onlara, çözemedikleri problemi çözsünler diye yük bindirmeyin. Hash'li dosyayı yıllık önbelleğe koymak başka bir karardır; HTML'i yeniden doğrulatmak başka. İkisini aynı başlık altında ezmek yine aynı yere çıkar: kafası karışık teslimat düzeni.
Sürümleme yoksa geçici olarak ne yaparsınız?
İdeal cevap belli: sürümleme ekleyin. Ama her proje o kadar temiz başlamıyor. Eski kurumsal yapı, elle yüklenen dosyalar, build süreci olmayan statik çıktı veya karmaşık tema sistemi yüzünden dosya adını hash'lemek hemen mümkün olmayabilir. Bu durumda “o zaman bir yıllık cache basalım” demek yerine daha geçici, daha dürüst bir strateji kurmak gerekir.
İlk seçenek süreyi düşürmektir. Dosya adı değişmiyorsa ve CSS gerçekten sık güncelleniyorsa bir saat, birkaç saat veya en fazla bir gün gibi daha makul ömürler kullanırsınız. Bu hız tarafında mükemmel çözüm değildir; ama deployment kaosundan iyidir. Özellikle küçük ekiplerde ve sık içerik güncellemesinde kontrollü kısa süre çoğu zaman güvenli başlangıç noktası olur.
İkinci seçenek, tam hash'li yapı kuramasanız bile sürüm parametresini disiplinli kullanmaktır. Evet, dosya adı hash kadar güçlü değildir; ama build kısıtı varsa app.css?v=20260308 gibi açık ve tutarlı sürümleme hiç sürümsüz yaşamaktan daha iyidir. Burada kritik olan şey parametreyi rastgele değil, deployment akışının parçası olarak değiştirmektir. Elle unutulan versiyon parametresi güvenli sistem sayılmaz.
Üçüncü seçenek, kritik dosyaları önce ayırmaktır. Her şeyi aynı anda düzeltmeye çalışma. En sık değişen CSS, ana layout'u taşıyan dosya, ortak JavaScript giriş paketi ve tekrar kullanılan temel medya varlıkları ilk hedef olabilir. Önce bunları temiz yönetir, geri kalanını daha sonra toplarsınız. Bu yaklaşım özellikle eski projelerde işe yarar; çünkü tüm teslimat zincirini tek seferde sökmeye kalkmaz.
Sürümleme yoksa dünya bitmiyor. Ama strateji değişiyor. Daha kısa ömür, daha sık doğrulama ve daha dikkatli deployment gerekiyor. Geçici çözümü kalıcı çözüm gibi pazarlamamak da önemli. Bir noktadan sonra gerçekten dosya yaşam döngüsünü düzeltmek zorundasınız; yoksa önbellek ayarı sürekli savunma yapan, hiçbir zaman rahat çalışmayan bir sisteme dönüşür.
Apache üzerinde nasıl uygulanır?
Bu site Apache kullandığı için örnekleri doğrudan Apache mantığıyla vermek daha yararlı. En sık ihtiyaç duyulan şey mod_headers ve gerekirse mod_expires.
Eğer kontrolü açık biçimde elinize almak istiyorsanız, önbelleği dosya türü ve dosya adı desenine göre yönetmek daha okunur olur.
<IfModule mod_headers.c>
<FilesMatch "\.html$">
Header set Cache-Control "no-cache, must-revalidate"
</FilesMatch>
<FilesMatch "\.[a-f0-9]{8,}\.(css|js)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
<FilesMatch "\.(css|js)$">
Header set Cache-Control "public, max-age=3600"
</FilesMatch>
<FilesMatch "\.(png|jpg|jpeg|gif|webp|svg|ico|woff2?)$">
Header set Cache-Control "public, max-age=2592000"
</FilesMatch>
</IfModule>
Bu örneğin mantığı basit: HTML yeniden doğrulansın. Hash'li CSS ve JavaScript uzun yaşasın. Hash taşımayan CSS/JS daha kısa yaşasın. Görseller orta-uzun aralıkta önbelleklensin. Gerekiyorsa proje ritmine göre bu süreleri değiştirirsiniz.
Alternatif olarak mod_expires ile tür bazlı süre verebilirsiniz; ama tür bazlı ayar tek başına hash ayrımı yapmadığı için bazen fazla kaba kalır. O yüzden Apache tarafında yalnızca uzantıya bakmak yerine mümkünse sürümlü dosyaları da ayırmak daha güvenli.
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/html "access plus 0 seconds"
ExpiresByType text/css "access plus 1 hour"
ExpiresByType application/javascript "access plus 1 hour"
ExpiresByType image/webp "access plus 30 days"
ExpiresByType image/png "access plus 30 days"
ExpiresByType image/jpeg "access plus 30 days"
ExpiresByType font/woff2 "access plus 30 days"
</IfModule>
Bu ikinci yöntem daha kolay; ama yine aynı uyarı geçerli: dosyalar hash'li geliyorsa uzun süreyi daha rahat artırırsınız, gelmiyorsa dikkatli olursunuz.
En sık görülen yanlış kurulumlar neler?
Birinci hata: her dosyaya aynı başlığı vermek. Kolay görünür, iyi sonuç vermez. HTML ile sürümlenmiş JS aynı ömre sahip olmamalı.
İkinci hata: sürümleme yokken immutable kullanmak. Bu, tarayıcıya fazla güven veren bir sözdür. Dosya adı değişmeden içeriği değişiyorsa bu sözü tutmazsınız.
Üçüncü hata: query string sürümlemesine gereğinden fazla yaslanmak. Çoğu modern kurulumda dosya adı hash'lemek daha sağlamdır. app.css?v=12 yaklaşımı tamamen işe yaramaz değil; ama CDN, ara katman önbellek ve dağıtım düzenine göre daha kırılgan olabilir. Dosya adına gömülü sürüm çoğu zaman daha nettir.
Dördüncü hata: CDN, reverse proxy ve origin sunucunun farklı başlıklar üretmesi. Origin bir şey söyler, CDN başka davranır, tarayıcı bambaşka süre görür. Sorun ortaya çıkınca yalnızca uygulama tarafına bakılır ve asıl neden atlanır. Özellikle çok katmanlı teslimatta Cache-Control, CDN davranışı ve gerekiyorsa s-maxage birlikte düşünülmeli.
Beşinci hata: servis çalışanı varsa onu önbellek politikasından ayrı düşünmek. Service worker devredeyse tarayıcı başlıkları tek başına her şeyi anlatmaz. Bu sitede o yapı yoksa mesele değil; ama uygulama tarafında varsa, “header doğruydu ama kullanıcı hâlâ eski dosyayı görüyor” sorusunun cevabı orada olabilir.
Doğru çalıştığını nasıl test edersiniz?
Yapılandırmayı yazıp bırakmayın. Gerçekten çalıştığını kontrol edin.
İlk kontrol yeri tarayıcı geliştirici araçlarıdır. Network panelinde HTML isteğini seçin; Cache-Control, ETag, Last-Modified ve yanıt davranışına bakın. Sonra CSS ve JavaScript dosyalarını açın. Hash'li dosyalar gerçekten uzun ömürlü başlık alıyor mu? HTML yeniden doğrulanıyor mu? Burada gördüğünüz tablo, teoriyi sahaya bağlar.
İkinci kontrol deployment testidir. Küçük bir CSS veya JS değişikliği yayınlayın. Dosya adı değişiyorsa yeni HTML bunu hemen referanslıyor mu? Farklı tarayıcı profilleriyle deneyin. Gizli sekme, normal sekme, hard refresh, normal refresh. Ama tek test olarak hard refresh'e güvenmeyin; kullanıcı her seferinde öyle davranmaz.
Üçüncü kontrol komut satırıdır. Özellikle Apache veya CDN katmanında neler döndüğünü görmek için başlıkları düz metin görmek faydalıdır.
curl -I https://hafifkod.com/
curl -I https://hafifkod.com/css/style.css
curl -I https://hafifkod.com/img/tarayici-onbellekleme-nasil-yapilir.webp
Burada görmek istediğiniz şey dosya rolüne göre değişen başlıklardır. Her yanıt aynıysa strateji muhtemelen fazla geneldir. Hiçbiri beklendiği gibi değilse sunucu katmanı ile uygulama katmanı birbirini eziyor olabilir.
Dördüncü kontrol kullanıcı etkisidir. Sayfalar arası geçişte ortak dosyalar yeniden taşınmıyor mu? Güncelleme sonrası eski arayüz şikâyeti azalıyor mu? Burada yalnızca teknik doğrulama değil, operasyon rahatlığı da ölçülür.
Hangi durumda strateji değişir?
Bu soru önemli. Her site aynı cache politikasını hak etmez.
Eğer içerik sitesi işletiyorsanız ve ortak CSS/JS dosyaları tüm sayfalarda tekrar kullanılıyorsa, uzun ömürlü statik cache çok değer üretir. Eğer sık deployment yapan, tek sayfa uygulaması ağır bir paneliniz varsa sürümleme disiplini daha da kritik hale gelir. Eğer çok sayıda editör kontrolsüz medya yüklüyorsa görsel stratejisinde daha dikkatli olmanız gerekir. Dosya adı değişmiyor ama içerik sık değişiyorsa agresif önbellek riski büyür.
Özel durumlar da var. Kullanıcıya özel hassas veriler, oturum ekranları, ödeme adımları veya çok sık değişen kişisel içerikler daha farklı düşünülür. O alanlarda salt performans değil, güvenlik ve veri tutarlılığı da devreye girer. Yani aynı başlığı tüm uygulamaya yaymak yerine, ekran tipine göre farklılaştırmak çoğu zaman daha olgun bir yaklaşımdır.
Bir de mobil ağ gerçeği var. Yavaş bağlantıda iyi önbellek farkı daha sert hissedilir. Masaüstünde tolere edilen gereksiz yeniden indirme, mobilde doğrudan deneyim maliyetine dönüşür. Bu nedenle “bizde masaüstünde hızlı açılıyor” cümlesi tek başına yeterli savunma değildir.
Pratikte ilk neyi yapmalısınız?
Eğer projede şu an her şey birbirine karışmışsa, en mantıklı başlangıç noktasını söyleyeyim: önce HTML ile statik varlıkları ayırın. Sonra build çıktınızın gerçekten sürümlü olup olmadığına bakın. Daha sonra yalnızca hash'li dosyalarda agresif cache'e gidin. Son adımda test edin; ama yalnızca kendi tarayıcınızda değil, temiz profil ve yeni istek akışıyla da test edin.
Bu sıra küçük görünür, ama işin omurgası budur. Önce dosya yaşam döngüsünü anlamadan başlık basarsanız hız kazanma umuduyla yayın kaosu satın alırsınız. Önce yaşam döngüsünü düzeltirseniz, önbellek gerçekten yardım etmeye başlar.
İyi kurulan önbellek stratejisi yalnızca ikinci ziyareti hızlandırmaz. Deploy sonrası tutarlılığı artırır, sayfalar arası geçişi toparlar, aynı dosyaların gereksiz taşınmasını azaltır ve destek tarafında “bende farklı görünüyor” tipindeki dağınık şikâyetleri düşürür. O yüzden konu yalnızca performans puanı değildir; teslimat kalitesidir.