Yazılar · Önbellek · Deploy · Hostinger
Deploy ettim ama site hâlâ eski hâlini gösteriyor: önbellek damgası neden şart
Tarayıcı, max-age dolana kadar sunucuya hiç sormaz; must-revalidate bunu değiştirmez, yalnız süre dolduktan sonra doğrulamayı zorunlu kılar. HTML taze gelip CSS bayat kalınca sayfa kırık görünür. Çare varlık adresine dosya içeriğinden türeyen bir damga eklemek (site.css?v=8f3a1c): dosya değişince adres değişir, değişmezse önbellek avantajı korunur. HTML'i ise ne tarayıcıda ne CDN'de uzun süre tutma.
Bir müşteri sitesinde başlık ve altbilginin kimliğini değiştirdim, canlıya bastım, kontrol ettim: HTML doğru, CDN'deki CSS doğru. Ama ekranda başlık hâlâ eski beyaz hâliyle duruyordu. İnsanın ilk yaptığı şey deploy günlüklerinde dolaşmak oluyor; oysa sorun yayında değil, teslimde. Tarayıcı yeni HTML'i almış, üstüne bir hafta önce indirdiği CSS'i giydirmişti. Bu yazı o tuzağın mekanizmasını ve bir daha yaşanmaması için kurduğum sistemi anlatıyor.
must-revalidate seni kurtarmıyor
Çoğu sitede varlıklar şu başlıkla servis edilir:
cache-control: public, max-age=604800, must-revalidate
Buradaki must-revalidate ifadesi, içerik bayatladıktan sonra tarayıcının sunucuya sormasını zorunlu kılar. Yani yedi gün dolana kadar tarayıcı hiçbir şey sormaz, yerel kopyayı kullanır. HTML genelde max-age=0 ya da no-cache ile geldiği için her zaman tazedir; uyumsuzluk tam da bu yüzden ortaya çıkar. İkisi de eski olsa sayfa en azından tutarlı görünürdü.
Aynı tuzağa kendi sitemde de düştüm: kaydırma animasyonlarını değiştirip yükledim, ölçüm "animasyon uygulanmamış" dedi. CSS otuz gün önbellekliydi ve tarayıcı eskisini servis ediyordu. Kod doğruydu, ölçüm yanlış değildi; ikisinin arasındaki önbellek eskiydi.
Çözüm: içerik damgası
Dosya adını değiştirmeye gerek yok, sorgu dizesi yeter. Damga zaman değil içerik olmalı; Date.now() her derlemede değişir ve önbelleği boşuna öldürür.
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
function surum(yol) {
const veri = readFileSync(yol);
return "?v=" + createHash("md5").update(veri).digest("hex").slice(0, 8);
}
// <link rel="stylesheet" href="/assets/site.css?v=202609040436${surum("assets/site.css")}">
Dosya değişince damga değişir, adres değişir, önbellek kendiliğinden kırılır. Dosya değişmezse damga aynı kalır ve yedi günlük ya da otuz günlük önbellek avantajı korunur. script-src 'self' gibi bir CSP kuralı sorgu dizesinden etkilenmez. Kendi sitemde bunu dosya değişim zamanından türetiyorum; deploy'dan önce tek komutla bütün sayfalara basılıyor ve PDF gibi "aynı adla değişen" dosyalar da listede.
HTML'i uzun önbellekleme, CDN'de hiç
Damga sistemi yalnız HTML her zaman taze geliyorsa çalışır. Bu yüzden HTML için no-cache, must-revalidate kullanıyorum; tarayıcı her seferinde sorar, sunucu değişmediyse 304 döner, maliyet birkaç yüz bayttır. CDN tarafında ayrı bir tuzak var: Hostinger'ın edge katmanı HTML'i tutarsa deploy sonrası eski HTML yeni varlıkları çağırır, bunun tam tersi de olur. Bir Next.js sitesinde ISR sayfalarına basılan s-maxage=3600 yüzünden edge bir saat boyunca artık var olmayan chunk'ları isteyen eski HTML servis etti ve sayfa hidrasyonda kırıldı. Halka açık HTML için makul ayar s-maxage=60, stale-while-revalidate=300; admin ve API için private, no-store.
Bir seferlik istisna: immutable'dan dönüş
Bir dosya daha önce immutable, max-age=31536000 ile önbelleklendiyse sonradan başlığı değiştirmek her tarayıcıda yetmeyebilir; bazıları immutable'ı çok ciddiye alır. O durumda tek garantili yol adresi değiştirmektir: site.css yerine site.v2.css. Eski adı 404'e düşürmemek için bir süre ikisini birlikte tut.
Teslimden önce nasıl doğrulanır
Kendi tarayıcında "düzeldi" görmek kanıt değil; sen büyük ihtimalle sert yenileme yapmışsındır. İki soru sorulur: CDN'de yeni dosya var mı ve sayfa gerçekten hangi sürümü yüklüyor?
// CDN'de yeni dosya var mı? (önbelleği atla)
const r = await fetch("/assets/site.css?v=202609040436?cb=" + Date.now(), { cache: "reload" });
(await r.text()).includes("YENI_KURALIN_ADI"); // true olmalı
// Sayfanın GERÇEKTEN yüklediği sürüm
document.querySelector('link[href*="site.css"]').getAttribute("href");
Bir de ölçüm tuzağı: animasyonlu bir bileşeni ekran dışı bir iframe içinde ölçme. Görünmeyen iframe'de CSS animasyonu başlamaz; animation-fill-mode: both ve gecikme varsa öğe ilk karede takılı kalır ve sen bunu "eski CSS" sanırsın. Animasyonlu durumu her zaman üst düzey, görünür sayfada ölç.
Bu yazı neye dayanıyor
İki gerçek olay: 30 Ağustos 2026'da bir müşteri sitesinin kimlik değişikliği ve 14 Ağustos 2026'da kendi sitemin animasyon güncellemesi. İkisinde de HTML ve CDN doğruydu, ziyaretçinin tarayıcısı eskiyi giydiriyordu. Damga sisteminden sonra aynı sınıftan bir sorun bir daha çıkmadı.
Yazılım ve sistem mimarı, AlgowAI'da COO. Bu yazıdaki her şey canlı bir sistemde ölçülerek yaşandı; tahmin yok. Sorun sende de varsa yaz, bakarım.