Versioned Asset Nedir? CSS ve JS Cache Busting Rehberi
Bir ekip style.css?v=2 olarak versiyonladığı CSS dosyasını deploy etti. Sunucu yeni dosyayı gönderiyor; ama kullanıcıların bir kısmı eski tasarımı görmeye devam ediyor. Geliştirici araçlarında 200 yanıtı görünüyor — dosya gelmiş gibi. Soruna bakıldığında anlaşılıyor ki önünde duran CDN, query string'i önbellek anahtarına dahil etmeyecek biçimde yapılandırılmış. ?v=1 ve ?v=2 CDN için aynı kaynak. Eski yanıt TTL dolana kadar servis edildi; hiçbir hata mesajı, hiçbir uyarı yoktu.
Cache busting, tarayıcıyı ve ara önbellekleme katmanlarını önbelleklenmiş bir dosyayı bırakıp yeni versiyonu almaya zorlamak anlamına gelir. Terim basit görünse de uygulamada iki farklı yaklaşım birbirinin yerine kullanılır — ve bu iki yaklaşımın CDN katmanındaki davranışı birbirinden tamamen ayrışır. Yanlış seçim, tutarlı olmayan kullanıcı deneyimlerine ve geliştiricilerin kolay tespit edemeyeceği sessiz hatalara zemin hazırlar.
Versioned asset stratejisinin merkezinde şu soru vardır: bir dosya değiştiğinde bunu tüm önbellekleme katmanlarının — tarayıcı, CDN, proxy — güvenilir biçimde fark etmesini nasıl sağlarsınız? Yanıt, URL'nin nasıl yapılandırıldığına bağlıdır.
Query string versiyonlamanın sessiz CDN tuzağı
Query string ile versiyonlama en sezgisel yaklaşımdır: dosya adı sabit kalır, sonuna değişen bir parametre eklenir — main.js?v=2, style.css?bust=20260417 veya app.js?rev=abc123. Tarayıcı URL'nin değiştiğini görür ve yeni istek gönderir. Bu katmanda yöntem işe yarar.
Sorun CDN katmanında başlar. Birçok CDN, varsayılan yapılandırmasında önbellek anahtarını URL'nin yalnızca path bölümüne dayandırır; query string'i yoksayar. AWS CloudFront bu yapılandırmaya varsayılan olarak gelir — query string'i iletmek için dağıtım ayarlarında açıkça yapılandırma gerekir. Fastly ve Varnish kurulumlarında da benzer bir varsayılan davranış söz konusudur. Cloudflare query string'i varsayılan olarak önbellek anahtarına dahil eder, ama normalizasyon kurallarına bağlı olarak parametre sırasındaki farklılıklar farklı önbellek girişleri oluşturabilir.
Sonuç: style.css?v=1 CDN'de önbelleğe alındı. Siz style.css?v=2'yi yayına aldınız. CDN yapılandırması query string'i yoksayıyorsa, TTL dolana kadar tüm kullanıcılara v=1 yanıtı gönderilir. Hata kaydı yok, 4xx yok, yalnızca eski içerik. Bunu test ortamında yakalamak zordur çünkü test genellikle CDN'siz yapılır; production'da sorun görünür hale gelir.
Query string versiyonlaması, tüm önbellekleme katmanlarının doğru yapılandırıldığını varsayar. Bu varsayım tek bir ekip tarafından tam kontrolü olmayan ortamlarda — üçüncü parti CDN hizmetleri, kurumsal proxy'ler, ISP düzeyinde önbellekler — sağlanamaz.
Content hash parmak izi — URL'nin içerik taşıyıcısı haline gelmesi
Content hashing dosya adını değiştirir: style.css, style.b7c9d2e1.css olur. Hash değeri dosya içeriğinden türetilir — genellikle çıktı byte'larının MD5 veya SHA-256 özeti. İçerikte tek bir byte değiştiğinde hash değişir, dolayısıyla URL değişir.
Bu fark yapısaldır. Her önbellekleme katmanı — tarayıcı, CDN, proxy, service worker — URL'yi kaynak kimliği olarak kullanır. style.css?v=1 ile style.css?v=2 aynı path'i paylaşır; CDN için aynı kaynaktır. style.b7c9d2e1.css ile style.a1b2c3d4.css ise birbirinden tamamen bağımsız iki kaynaktır. CDN her ikisini de bağımsız TTL'lerle önbelleğe alır; eski hash'li dosya zamanla eviksiyon alır, yeni hash'li dosyanın kendi önbellek yaşam döngüsü başlar.
Bu yapı, tarayıcı önbellekleme stratejisinin en tutarlı biçimidir. İçerik değiştiğinde URL değişir; URL değişmediği sürece içerik değişmemiştir — bu garantiyi query string versiyonlaması veremez. Immutable cache direktifinin yalnızca content hash ile güvenli olmasının temel nedeni de budur: hash değişmeden URL değişmez, URL değişmeden içerik değişmemiş demektir.
Determinizm önemlidir. Aynı içerikten her build'de aynı hash üretilmesi gerekir. Zaman damgası, rastgele salt veya modül sırasındaki tutarsızlık hash'i kararsız hale getirir; içerik değişmese de her deploy'da hash değişir. Bu durum gereksiz önbellek iptali anlamına gelir — kullanıcılar değişmemiş dosyaları yeniden indirir. Modern bundler'lar bunu doğru yapılandırıldığında ele alır, ama özellikle circular dependency veya dynamic import zincirlerinde hash kararlılığı bozulabilir.
Webpack, Vite ve Rollup'ta hash çıktısını yapılandırmak
Üç büyük bundler da content hash üretimini destekler. Token sözdizimi farklılık gösterir ve bu farkları anlamak önemlidir.
Webpack'te üç farklı hash token'ı vardır. [hash] derleme genelinde hesaplanır — herhangi bir dosya değiştiğinde tüm chunk'ların hash'i değişir; bu uzun vadeli önbellekleme için kullanışsızdır. [chunkhash] chunk bazında hesaplanır; yalnızca o chunk değiştiğinde değişir. [contenthash] ise dosya içeriğine göre hesaplanır; CSS çıktıları için özellikle uygundur çünkü JS bağımlılıklarından bağımsız olarak hesaplanır.
// webpack.config.js
module.exports = {
output: {
filename: '[name].[contenthash:8].js',
chunkFilename: '[name].[contenthash:8].js',
assetModuleFilename: 'assets/[name].[contenthash:8][ext]'
}
};
Vite, Rollup'un hash mekanizmasını kullanır. [hash] token'ı Vite'da içerik bazlıdır ve CSS varlıkları için de otomatik uygulanır:
// vite.config.js
export default {
build: {
rollupOptions: {
output: {
entryFileNames: 'assets/[name].[hash].js',
chunkFileNames: 'assets/[name].[hash].js',
assetFileNames: 'assets/[name].[hash][extname]'
}
}
}
};
Hash uzunluğu: varsayılan 8–20 karakter, çarpışma olasılığı açısından yeterlidir. 4 karaktere kısaltmak URL uzunluğunu anlamlı ölçüde azaltmaz; ama küçük projelerde bile düşük ihtimalli çarpışma riskini artırır. Varsayılan uzunlukları korumak en güvenli tercihtir. Bundle analizi sırasında hangi modüllerin hangi chunk'ı etkilediğini izlemek, beklenmedik hash değişikliklerinin kaynağını bulmayı kolaylaştırır.
HTML referanslarının her deploy'da otomatik güncellenmesi
Hash'li dosya adları üretmek sorunun yalnızca ilk yarısıdır. HTML hâlâ style.css veya main.js'ye referans veriyorsa hash üretiminin hiçbir faydası yoktur. Her deploy'da HTML'nin yeni hash'li dosya adlarına işaret etmesi gerekir.
Build araçları bunu manifest dosyaları ve şablon enjeksiyonu aracılığıyla çözer. Webpack, asset-manifest.json veya webpack-manifest-plugin ile mantıksal isimden hash'li isme dönüşüm tablosu üretir. html-webpack-plugin, çıktı HTML'sine doğru script ve link etiketlerini otomatik olarak enjekte eder. Vite'ın yerleşik HTML işleme mekanizması aynı görevi üstlenir; vite build çıktısında HTML zaten hash'li URL'lere referans verir.
Sunucu taraflı render eden uygulamalarda manifest dosyası deploy anında okunur ve şablona aktarılır. Bu akış tutarlı çalışmazsa — örneğin eski manifest önbellekte kalırsa — HTML yeni hash'lere referans verirken sunucu eski hash'li dosyaları sunar ve 404 hatası oluşur. Manifest dosyasının her deploy'da temiz üretildiğini CI/CD pipeline'ında doğrulamak bu riski bertaraf eder.
HTML dosyası asla uzun önbellekle sunulmamalıdır. Cache-Control: no-cache ile her istekte doğrulama yapılması, kullanıcıların her zaman güncel hash referanslarını içeren HTML almasını garantiler. Varlıklar uzun önbellekle — hatta immutable ile — sunulurken, HTML kısa veya sıfır önbellekle tutulur. Bu asimetri, hem maksimum önbellekleme verimliliğini hem de anlık güncellemeyi mümkün kılar. Cache-Control direktifleri ve ETag doğrulamasının birlikte nasıl çalıştığını anlamak bu asimetrinin neden gerekli olduğunu netleştirir.
Uzun önbellek süresi ile anlık güncelleme — cache busting döngüsü
Content hash stratejisi, önbellek invalidasyonu sorununu deployment pipeline'ına taşır. Sunucuda veya CDN'de aktif invalidasyon yapmak yerine URL değişikliği yoluyla pasif invalidasyon gerçekleşir. Eski hash'li URL zamanla eviksiyon alır; yeni hash'li URL kendi yaşam döngüsüyle başlar. Ancak bu akışın çalışması için deployment adımlarının her seferinde aynı sırayla eksiksiz yürümesi şarttır.
CI/CD'de build çıktı dizinini (dist/, build/, .next/) deploy'lar arasında önbelleğe almak yaygın bir optimizasyondur. Sorun şu ki kaynak dosyalar değiştiğinde önbellekteki build çıktısı geçersiz hale gelir ama önbellek anahtarı güncellenmediyse eski çıktı kullanılmaya devam edebilir. Bağımlılıkların değişmediği koşullarda build önbelleğini korumak için önbellek anahtarını lockfile içeriğine (package-lock.json veya yarn.lock hash'ine) bağlamak güvenli bir pratiktir; kaynak dosyalar değiştiğinde ise build önbelleğinin devre dışı bırakılması gerekir.
Birkaç deploy sonrasında sunucuda artık hiçbir HTML'nin referans vermediği eski hash'li dosyalar birikir. Bu dosyalar zararsızdır ama disk alanı tüketir. Deployment sonrası bir temizlik adımı — mevcut manifest ile bir öncekini karşılaştırarak referanssız kalan dosyaları silen bir script — süreci düzenli tutar. Code splitting ve tree shaking ile üretilen birden fazla chunk dosyasının bulunduğu projelerde bu temizlik daha da önemlidir; her deploy onlarca orphan dosya bırakabilir.
Versioned asset stratejisi, önbellekleme ile güncellik arasındaki gerilimi çözmenin yapısal yoludur. Query string versiyonlaması bu gerilimi taşır; tüm katmanların doğru yapılandırılmasına bağımlıdır ve bu bağımlılık kırılgandır. Content hashing ise gerilimi URL mimarisine gömer — doğru yapılandırıldığında CDN yapılandırması, proxy davranışı veya tarayıcı versiyonu fark etmeksizin çalışır.
Uzun vadeli önbelleklemenin güvenli olduğu tek senaryo, URL'nin içeriği tam olarak tanımladığı senaryodur. Content hash bunu hash uzunluğunun güvenilirlik sınırları içinde garanti eder. Bu garantinin üzerine max-age=31536000 ve immutable inşa edilebilir; altında olmadan bu direktifler kırılgan kalır.