Yeni bir tasarım yayımlandığı hâlde bazı ziyaretçilerin eski CSS dosyasını kullandığını düşünün. Sunucuda dosyanın değiştirilmiş olması, tarayıcıdaki taze kopyayı kendiliğinden geçersiz kılmaz. Statik dosya önbellekleme düzeni; Cache-Control, immutable, ETag, Last-Modified ve içerik değiştiğinde değişen dosya adreslerini birlikte ele almalıdır.
Hedef, değişmeyen dosyaların tekrar indirilmesini azaltırken yeni sürümlerin doğru adreslerden alınmasını sağlamaktır. Herkese açık, içerik hash’i taşıyan bir logo uzun süre önbelleklenebilir; güncel dosya adreslerini dağıtan HTML ise kısa tazelik süresi veya yeniden doğrulama ister. Kişiye özel yanıtların paylaşılabilirliği ayrıca belirlenmelidir.
Bu rehber önce HTTP önbellekleme ve doğrulama davranışını açıklar; ardından Nginx, Apache, Express ve S3/CloudFront için uygulanabilecek düzeni gösterir. Kodlar herkese açık statik dosyalar içindir. Dinamik HTML, oturumlu API ve özel medya yanıtları aynı public politikaya alınmamalıdır.
Tazelik Süresi ve Yeniden Doğrulama
max-age, yanıtın kaç saniye taze kabul edileceğini belirtir. Önbellekte uygun bir taze kopya varsa tarayıcı yeni ağ isteği yapmadan onu kullanabilir. Bu süre dosyanın her açılışında yeniden başlamaz; yanıtın yaşı ve varsa ara önbellekten gelen Age bilgisi hesaba katılır. Kopya silinmişse veya istek koşulları yeniden kullanıma izin vermiyorsa tekrar ağ erişimi gerekir.
Sunucudaki dosyayı aynı URL altında değiştirmek, mevcut tazelik süresini sona erdirmez. immutable bunu düzeltmez; o URL’deki yanıtın tazelik süresince değişmeyeceğini bildirir ve destekleyen istemcilerde normal yenilemedeki gereksiz doğrulamayı da azaltabilir. Süreyi max-age gibi başka bir ayar belirler. Kullanıcının zorunlu yenilemesi bu davranışı aşabilir; önbelleğin dosyayı sonsuza kadar tutması garanti edilmez.
Yeniden doğrulama gerektiğinde tarayıcı sakladığı ETag değerini If-None-Match, Last-Modified değerini ise If-Modified-Since ile gönderebilir. Değişiklik yoksa GET/HEAD isteğine 304 Not Modified yanıtı gelebilir. Dosya gövdesi tekrar aktarılmaz ama istek, yanıt başlıkları ve ağ gecikmesi hâlâ vardır. Her önbellek kullanımı doğrulama gerektirmez.
ETag mı, Last-Modified mı? Hangisini Ne Zaman Kullanmalı?
ETag, sunucunun seçilen temsil için ürettiği opak bir doğrulayıcıdır; mutlaka içerik hash’i değildir. Güçlü ETag bayt düzeyindeki farklılıkları ayırmalıdır; zayıf ETag anlamsal olarak eşdeğer temsiller için kullanılabilir ve If-None-Match doğrulamasında 304 yanıtını destekler. Last-Modified son değişiklik zamanını bildirir; HTTP tarihinin saniye çözünürlüğü ve dağıtımda korunmayan dosya zamanları sınırlamalar yaratabilir. İki koşul birlikte gönderilirse If-None-Match önceliklidir.
URL’si değişmeden güncellenen bir dosyada, kabul edilebilir gecikmeye göre kısa max-age veya no-cache kullanın. Doğrulayıcılar ancak kontrol yapıldığında yararlıdır; taze kopyayı kendiliğinden denetletmezler. İçerik değiştiğinde URL’si değişen dosyalarda uzun max-age ve immutable uygundur. Bu dosyalarda ETag/Last-Modified tutulabilir; tazelik sona erdiğinde doğrulama için hâlâ işe yarayabilirler.
Çok sunuculu bir yapıda aynı temsil için tutarlı doğrulayıcılar üretin. Aynı dosya her düğümde farklı ETag veya değişim zamanı alıyorsa gereksiz tam yanıtlar oluşabilir. İçerik temelli ETag bir seçenektir; güçlü etiketler gzip, Brotli ve sıkıştırılmamış farklı bayt dizileri için de farklı olmalıdır. ETag’ı kapatmak yerine önce kaynağını inceleyin; yalnızca güvenilir Last-Modified kullanmak da mümkündür, ancak tarih temelli sınırlamalar devam eder.
Immutable Ne Zaman Harika, Ne Zaman Sakıncalı?
immutable için temel koşul, içerik değiştiğinde yeni URL yayımlamaktır. Örneğin app.abc12345.css bir sürümü gösteriyorsa bu ad altında farklı CSS sunmayın. public, max-age=31536000, immutable yalnızca herkese açık ve bu koşulu sağlayan dosyalar için bir yıllık tazelik örneğidir; ölçülmüş bir hız veya trafik tasarrufu vaadi değildir.
HTML’ye veya aynı adla değişen CSS’ye uzun tazelik ve immutable vermek güncellemeleri geciktirebilir. Origin’de daha sonra süreyi kısaltmak, tarayıcıda zaten taze olan kopyanın başlıklarını değiştirmez. CDN purge işlemi de tarayıcı belleğindeki kopyayı geri çağıramaz. Bu nedenle değişebilir URL’nin politikasını baştan belirleyin; acil bir varlık değişikliğinde yeni dosya adresi yayımlayın.
Başlıkların genel anlamı için MDN Cache-Control açıklamalarını, sağlayıcıya özgü davranış için Cloudflare Origin Cache Control belgelerini inceleyin. Cloudflare’da immutable tarayıcı davranışını etkiler; edge önbelleğin tazeliğini tek başına belirlemez. Cache Rules gibi ayarlar origin politikasını değiştirebilir.
Versiyonlama (Fingerprinting): Her Şeyi Bir Dosya Adına Sığdırmak
Fingerprinting, dosya içeriğinin hash’ini dosya adına veya yoluna taşır: app.css yerine app.abc12345.css gibi. İçerik değişince yeni adres üretilir; HTML ve dosyayı çağıran diğer varlıklar da yeni adrese geçirilir. Aynı içerik için aynı adresi korumak, her dağıtımda değişen zaman damgalarına kıyasla sürümler arasında yeniden kullanımı kolaylaştırır.
Webpack, Vite, Rollup veya Parcel gibi araçların içerik hash’i üretimi ve varsayılanları sürüme ve yapılandırmaya göre değişir. Çıktı adlarını, HTML referanslarını, CSS içindeki görsel/font yollarını ve sonradan yüklenen JavaScript parçalarını kontrol edin. Hash görünümündeki bir ad tek başına içeriğin değişmediğini kanıtlamaz; bu sözleşmeyi build ve dağıtım hattı sağlamalıdır.
no-cache, saklamayı yasaklamaz; saklanan yanıt yeniden kullanılmadan önce başarılı doğrulama ister. Kısa max-age ise belirtilen süre içinde doğrulamasız yeniden kullanıma izin verebilir. Kişiye özel HTML’de private, no-cache ortak önbellekte saklamayı önler; saklamanın yasaklanması gerekiyorsa no-store kullanılır. Özel API ve medya erişimi de kimlik doğrulama ve CDN politikasıyla birlikte ele alınmalıdır.
Nginx ile Adım Adım: Hangi Dosyaya Ne Verelim?
Nginx konfigürasyonu için basit bir çatı
Bu Nginx örneği, root dizini yalnızca herkese açık dosyalar içeren bir statik site varsayar. /assets/ altındaki en az sekiz onaltılık karakterli hash adları uzun, diğer varlıklar beş dakikalık tazelik alır. Hash kuralı genel regex’ten önce gelir; Nginx eşleşen ilk regex location’ı kullanır. Mevcut ^~ veya tam eşleşmeli location kurallarınız sonucu değiştirebilir. Bölümü mevcut server yapılandırmasına uyarlayın; uygulama proxy/FastCGI yönlendirmelerinin yerine doğrudan koymayın.
# server {} icine; root yalnizca herkese acik statik dosyalari gostermeli.
# Hash'li kural genel statik kuraldan once gelmeli.
location ~* "^/assets/(?:[^/]+/)*[^/]+[.][0-9a-f]{8,}[.](?:css|js|mjs|png|jpg|jpeg|gif|webp|avif|svg|ico|woff2?)$" {
try_files $uri =404;
expires off;
etag on;
add_header Cache-Control "public, max-age=31536000, immutable";
}
location ~* "^/assets/.+[.](?:css|js|mjs|png|jpg|jpeg|gif|webp|avif|svg|ico|woff2?)$" {
try_files $uri =404;
expires off;
etag on;
add_header Cache-Control "public, max-age=300";
}
location = / {
try_files /index.html =404;
expires off;
etag on;
add_header Cache-Control "no-cache";
}
location ~* [.]html$ {
try_files $uri =404;
expires off;
etag on;
add_header Cache-Control "no-cache";
}
# Bu statik site orneginde diger mevcut dosyalar dogrulanarak kullanilir.
location / {
try_files $uri =404;
expires off;
add_header Cache-Control "no-cache";
}
Örnekteki [.] gerçek noktayı eşleştirir; hash uzunluğu ve alfabesi build çıktınızla uyuşmalıdır. JSON ve source map dosyaları otomatik olarak uzun süreli public varlık sayılmaz; bunların yayımlanabilirliğini ayrıca değerlendirin. / ve .html yanıtları doğrulamaya ayrılır; uzantısız uygulama rotalarının başlıkları kendi işleyicisinde ayarlanmalıdır. expires off, üst seviyeden gelen Expires/max-age üretimini kapatır. add_header kalıtımı nedeniyle gerekli diğer yanıt başlıklarını da koruyun. Hata yanıtlarında uzun immutable politikası verilmez; hata önbelleklemesini ayrıca test edin. style.css?v=123 gibi sorgu dizeli sürümleme de çalışabilir, ancak CDN cache key’i sürüm parametresini ayırt etmelidir.
Sıkıştırma ve Protokol Ayarları
Önbellek dışında kalan ağ aktarımlarında sıkıştırma ve protokol ayarları da önemlidir. Brotli/gzip sunarken Content-Encoding ve Vary: Accept-Encoding başlıklarını doğru üretin; güçlü ETag farklı kodlanmış gövdeleri ayırt etmelidir. İlgili kurulum ayrıntıları için Nginx TLS ve Brotli rehberine bakabilirsiniz. Bu ayarlar URL sürümlemesinin yerine geçmez.
Apache (.htaccess) ile Ayarlar: Kısa ve Net
Aşağıdaki .htaccess örneği yalnızca herkese açık dosyalar sunan statik DocumentRoot içindir; mod_headers ve uygun AllowOverride FileInfo/Indexes izinleri gerekir. Ana yapılandırmadaki DirectoryIndex index.html ise / isteği de HTML politikasını alır. Varsayılan no-cache korunur; dosya adı kuralları yalnızca 200, 206 ve 304 yanıtlarında kısa veya uzun varlık politikasına geçer. Böylece bulunamayan hash’li bir dosya uzun süreli immutable başlık almaz.
# Yalnizca herkese acik dosyalar iceren statik DocumentRoot icin .htaccess.
# mod_headers gerekli; AllowOverride FileInfo Indexes etkin olmali.
<IfModule mod_expires.c>
ExpiresActive Off
</IfModule>
Header onsuccess unset Cache-Control
Header always set Cache-Control "no-cache"
Header onsuccess unset Expires
Header always unset Expires
<FilesMatch "(?i)^.+[.](css|js|mjs|png|jpg|jpeg|gif|webp|avif|svg|ico|woff2?)$">
Header always set Cache-Control "public, max-age=300" "expr=%{REQUEST_STATUS} == 200 || %{REQUEST_STATUS} == 206 || %{REQUEST_STATUS} == 304"
</FilesMatch>
# app.abc12345.css gibi; genel kuraldan sonra uygulanir.
<FilesMatch "(?i)^.+[.][0-9a-f]{8,}[.](css|js|mjs|png|jpg|jpeg|gif|webp|avif|svg|ico|woff2?)$">
Header always set Cache-Control "public, max-age=31536000, immutable" "expr=%{REQUEST_STATUS} == 200 || %{REQUEST_STATUS} == 206 || %{REQUEST_STATUS} == 304"
</FilesMatch>
mod_expires yüklüyse bu alanda kapatılır; hem expires kuralları hem mod_headers ile çelişen max-age üretmeyin. Header’ın onsuccess ve always tabloları farklı olduğundan örnek önce mevcut alanı temizleyip tek politika yazar. Uygulama veya proxy yanıtlarına aynı kuralları taşımadan önce nihai başlıkları kontrol edin. Apache’nin güncel statik dosya ETag varsayılanı MTime ve Size’dır; düğümler arasında aynı dosyanın zaman ve boyut bilgisini tutarlı üretin. Varsayılanın her zaman inode içerdiğini varsaymayın.
Node.js/Express ile Başlıklar: Uygulamadan Vermek
Express 5 için bu CommonJS örneğini app.cjs olarak düşünün; Express paketinin kurulmuş olması gerekir. public/ yalnızca yayımlanabilir dosyaları içermelidir. Politika, istek URL’sindeki sorgu dizisine göre değil, gerçekten sunulan dosyanın yoluna göre setHeaders içinde seçilir. /assets/ dışındaki dosyalar ve HTML no-cache alır; cacheControl: false otomatik maxAge üretimini kapatır. Bu örnek aynı sunucudaki bir ters vekilin arkasında kullanılabilecek şekilde loopback’e bağlanır.
// app.cjs; Express 5. public/ yalnizca herkese acik dosyalar icermeli.
const express = require('express');
const path = require('node:path');
const app = express();
const publicRoot = path.join(__dirname, 'public');
const asset = /[.](css|js|mjs|png|jpg|jpeg|gif|webp|avif|svg|ico|woff2?)$/i;
const fingerprint = /^.+[.][0-9a-f]{8,}[.](css|js|mjs|png|jpg|jpeg|gif|webp|avif|svg|ico|woff2?)$/i;
app.use(express.static(publicRoot, {
etag: true,
lastModified: true,
cacheControl: false,
setHeaders: (res, filePath) => {
const relative = path.relative(publicRoot, filePath).split(path.sep).join('/');
const name = path.basename(filePath);
let policy = 'no-cache';
if (relative.startsWith('assets/') && asset.test(name)) {
policy = fingerprint.test(name)
? 'public, max-age=31536000, immutable'
: 'public, max-age=300';
}
res.setHeader('Cache-Control', policy);
}
}));
app.use((req, res) => {
res.set('Cache-Control', 'no-store').status(404).send('Not found');
});
app.listen(3000, '127.0.0.1');
express.static zayıf ETag üretir ve dosyanın değişim zamanını Last-Modified için kullanır; bunlar koşullu GET/HEAD doğrulamasında yararlıdır. Olmayan dosya son 404 işleyicisine ulaşır ve no-store alır. Dinamik veya kişisel yanıtların kurallarını kendi rotalarında tanımlayın. Süreç yönetimi ve dağıtım için PM2, systemd ve Nginx ile Node.js dağıtım rehberini inceleyebilirsiniz; yeni varlıkların hazır olması yine dağıtımın önkoşuludur.
S3, CloudFront ve CDN Dünyasında Kurulum
S3 nesnelerini yüklerken veya meta verisini düzenlerken Cache-Control politikasını belirleyin. Herkese açık ve değişmez sürüm anahtarı taşıyan dosyalar public, max-age=31536000, immutable alabilir. S3 Versioning’in açık olması tek başına kullanıcıya görünen URL’yi değiştirmez; yeni içeriği yeni nesne anahtarıyla yayımlayın veya istek ve cache key’inin sürümü açıkça ayırt ettiğini doğrulayın. İmzalı URL kullanılan özel medya için public varsayılanını kopyalamayın.
CloudFront’ta origin başlığını yazmak yeterli değildir: cache behavior ve cache policy içindeki minimum, varsayılan ve maksimum TTL değerlerini kontrol edin. CloudFront önbellek süresi belgelerine göre minimum TTL sıfırdan büyükse no-cache, no-store veya private bulunsa bile CloudFront en az o süre önbellekleyebilir. Bu direktiflere uyması gereken davranışlarda minimum TTL’yi sıfır seçin; hassas rotalarda önbelleği kapatma ve erişim kontrollerini birlikte planlayın. max-age tarayıcı, s-maxage ortak önbellek için farklı süreler tanımlayabilir; bir yıllık tarayıcı süresi edge’de bir yıl tutmayı gerektirmez. CDN’nin gerçekten döndürdüğü başlıkları ve hata/stale politikasını da doğrulayın.
Medya taşıma, erişim ve geçersizleştirme ilişkisi için WordPress medyayı S3’e taşıma ve CDN önbellekleme rehberine bakabilirsiniz. İmzalı URL, önbellek anahtarı ve doğrulama noktasının birlikte doğru çalışmasını gerektirir; dosya adında hash bulunması erişim denetimi sağlamaz.
Dağıtım Sırası: Önce Varlıklar, Sonra HTML
Önce yeni hash’li dosyaları yayımlayın ve erişilebilir olduklarını doğrulayın; ardından HTML ve diğer referansları değiştirin. Yeni HTML’nin işaret ettiği varlıklar mevcut ve bu sürümle uyumlu olmalıdır; değişmeyen dosyalar önceki adreslerini kullanabilir. Atomik sürüm klasörü veya sembolik bağlantı geçişi yararlı olabilir, fakat eski HTML’yi kullanan açık sayfalar ve sonradan yüklenen parçalar için eski dosyaların da erişilebilir kalması gerekir. Eski varlıkların saklama süresini önbellek, açık oturum ve geri dönüş ihtiyacına göre belirleyin.
VPS’te rsync ve sembolik sürümlerle CI/CD rehberi bu dağıtım düzenine yardımcı olabilir. Rutin hash’li varlık güncellemeleri eski adreslerin purge edilmesini gerektirmez; değişebilir HTML veya diğer referanslar için hedefli CDN geçersizleştirmesi gerekebilir. Geri dönüşte eski HTML’nin işaret ettiği dosyaları da koruyun.
AVIF/WebP gibi farklı görsel temsillerinde dosya adresi veya cache key’i doğru biçimi ayırt etmelidir. Aynı URL Accept başlığına göre değişiyorsa uygun Vary başlığını ve CDN varyant ayrımını sağlayın; yanıtı değiştiren sorgu parametrelerini anahtardan çıkarmayın. Görüntü optimizasyonu ve cache key tasarımı, dosya sürümleme ile biçim seçiminin birlikte ele alınmasını açıklar.
Test Etmeden Güvenme: DevTools, curl ve Küçük Tüyolar
DevTools Network bölümünde ilk yükleme, tekrar gezinme ve yenileme davranışlarını ayrı deneyin. Gerçek yeniden kullanımı incelerken Disable cache kapalı olmalıdır. Cache-Control, ETag, Last-Modified, Age ve varsa CDN durum başlıklarına bakın; disk/memory cache sonucu ile ağ üzerinden gelen 200 veya 304 yanıtını ayırın. Service worker varsa onun önbelleklemesini de ayrı inceleyin.
Aşağıdaki curl komutları başlık ve koşullu HEAD denetimi içindir. Örnek alan adı, dosya yolu ve ETag değerini kendi yanıtınızla değiştirin:
# Ornek alan adini ve dosya yollarini kendi sitenize uyarlayin.
curl -sS -I 'https://example.com/assets/app.abc12345.css'
curl -sS -I 'https://example.com/assets/app.css'
# Yukaridaki yanittan gercek ETag degerini, W/ ve tirnaklariyla kopyalayin.
curl -sS -I -H 'If-None-Match: "sunucudan-kopyalanan-etag"' 'https://example.com/assets/app.css'
curl -sS -I -H 'Cache-Control: no-cache' 'https://example.com/index.html'
İlk iki komut uzun ve kısa politikanın başlıklarını gösterir. Üçüncüde önceden dönen gerçek ETag eşleşirse 304 beklenir; uydurma etiketle 200 almak bir hata kanıtı değildir. Dördüncü istekte no-cache doğrulama talep eder, ama istemci doğrulayıcı göndermediğinden 304 beklemek doğru olmaz. Last-Modified için aynı denemeyi gerçek tarih ve If-Modified-Since ile yapın. Ayrıca normal GET ile gövdeyi, olmayan hash’li dosyanın 404 yanıtını, sorgu dizelerini ve CDN/origin farkını kontrol edin. curl tarayıcı önbelleğini taklit etmez.
Doğrulayıcıların anlamı için MDN ETag başvurusunu kullanabilirsiniz. Başlıkların doğru görünmesi tek başına yeni HTML’nin bütün varlıklara erişebildiğini veya CDN’nin aynı politikayı uyguladığını kanıtlamaz; dağıtım senaryosunu uçtan uca doğrulayın.
Örnek Senaryo: Aynı URL’de Güncellenen CSS
Bir CSS dosyasının aynı adla, orta uzunlukta max-age ve ETag ile sunulduğunu düşünün. Dosya değişince bazı kullanıcıların eski görünümde kalması, ETag bozuk olmasa da mümkündür: taze kopya için henüz doğrulama yapılmamış olabilir. CDN başlıkları, tarayıcı tazeliği ve service worker davranışını ölçmeden tek bir katmanı suçlamayın. Edge purge işleminin yerel tarayıcı kopyasını temizlemediğini de hesaba katın.
Bu senaryoda kalıcı düzen; build sırasında içerik hash’i üretmek, değişen dosyaları yeni adlarla yayımlamak, referansları güncellemek ve HTML’yi doğrulama veya kısa tazelikte tutmaktır. Yeni HTML’yi alan istemci yeni varlık adresini ister. Önceki HTML’yi kullananlar için eski dosyalar erişilebilir kalır; böylece sürüm geçişi eski kopyaları zorla geri çağırmaya dayanmaz.
Bu akışı staging’de sınarken eski HTML açıkken dağıtım yapmayı, gecikmeli parça yüklemesini ve önceki sürüme dönmeyi de deneyin. Geliştirme, staging ve canlı dağıtım rehberi ortamlar arasında uygulanacak kontroller için ek bağlam sunar.
Sık Yapılan Hatalar: Kısa Bir Tur ve Çözüm Yolları
Uzun tazelik verilen her dosyanın adresi gerçekten içeriğe bağlı mı kontrol edin. HTML’nin eski referansları beklenenden uzun taşıması, kısa hash’in örnek regex’e uymaması veya dosya uzantısının yanlış eşleşmesi farklı sorunlardır. Yanıtın hangi location, FilesMatch ya da Express işleyicisinden geldiğini belirleyin; birden fazla Cache-Control alanı ve çelişen max-age değerleri arayın.
Diğer sık sorunlar; düğümler arasında değişen doğrulayıcılar, cache key’inden çıkarılmış sürüm parametreleri, özel yanıtlara verilen public politikası ve eksik varlıkları uzun süre önbellekleyen hata ayarlarıdır. CDN hit görmek tarayıcının ağ isteği yapmadığı anlamına gelmez. Origin, edge ve tarayıcı katmanlarının her birini ayrı gözlemleyin.
Uygulama Kontrol Listesi
Önce içerik değişince değişen dosya adreslerini ve tüm referansları doğrulayın. Sonra yalnızca herkese açık, değişmez varlıklara uzun tazelik ve immutable verin; HTML ve değişebilir yanıtların güncellenme ihtiyacını belirleyin. ETag ve Last-Modified değerlerinin kararlılığını, sıkıştırılmış temsilleri ve CDN anahtarlarını birlikte kontrol edin.
Canlıya geçmeden önce normal ve koşullu GET/HEAD yanıtlarını, eksik dosyaları, yeni/eski HTML ile varlık erişimini ve geri dönüşü sınayın. Bu kontrollerden sonra tekrar indirme sayısı, aktarım boyutu ve gerçek kullanıcı gecikmesini ölçün. Önbelleklemenin başarısı yalnızca uzun bir max-age değeriyle değil, güncellemelerin doğru ulaşmasıyla değerlendirilir.





