Statik sitede önbellek stratejisi tek kuralla özetlenir: hash’li dosya adı taşıyan varlıklar Cache-Control: max-age=31536000, immutable alır, HTML Cache-Control: max-age=0, must-revalidate alır. İkisini karıştırmak deploy’u haftalarca görünmez kılar.
Sebep basit. Tarayıcı HTML’i uzun süre cache’lerse yeni deploy’un ürettiği yeni HTML’e hiç ulaşmaz, eski sayfayı eski asset referanslarıyla göstermeye devam eder.
Tek konfigürasyon hatası, haftalık görünmezlik
Statik dosyalara tek bir location bloğuyla, ayrım yapmadan expires 7d; yazan kurulum çok görülüyor. Bu satır hem _astro/index.4f2a91c3.jse hem index.htmle aynı yedi günü veriyor.
Sonuç: yeni içerikle deploy’u tamamlarsınız, sunucu doğru dosyayı sunar, ama ziyaretçinin tarayıcısı yerel diskteki eski index.htmli hâlâ geçerli sanıp sunucuya hiç sormaz. Deploy başarılı görünür, hiçbir hata çıkmaz, sadece kimse yeni içeriği görmez. Bunu fark etmenin tek yolu genelde “ben deploy ettim ama site değişmedi” şikayeti, o da genelde bir hafta sonra gelir.
Hash’li varlıklar neden bir yıl cache’lenir
Astro build’i her JS ve CSS dosyasının adına içerik hash’i ekliyor (_astro/index.4f2a91c3.js gibi). Dosya içeriği değişirse hash de değişiyor, dosya adı da değişiyor. Aynı ada iki farklı içerik hiç gelmiyor.
Bu yüzden bu dosyalara verilen süre sonsuza yakın olabilir:
location ~* \.(?:js|mjs|css|woff2)$ {
add_header Cache-Control "public, max-age=31536000, immutable" always;
}
immutable tarayıcıya “bu dosya asla değişmeyecek, süre dolmadan bile tekrar sorma” diyor. Desteklemeyen tarayıcıda zararı yok, sadece max-agee döner.
HTML her seferinde doğrulanmalı
HTML dosyasının adı sabit. index.html her deploy’da aynı URL’de duruyor, içeriği değişiyor. Buraya uzun max-age yazmak tarayıcıya “içerik değişmez” diye yalan söylemek.
location ~* \.html$ {
add_header Cache-Control "public, max-age=0, must-revalidate" always;
}
max-age=0 tarayıcıyı her seferinde sunucuya sormaya zorluyor, must-revalidate süre dolduğunda (burada anında) taze kopya almadan eski kopyayı kullanmasını engelliyor. ETag varsa sunucu çoğu zaman 304 döner, yeni bir indirme olmuyor, sadece küçük bir doğrulama isteği.
expires mi add_header mı
nginx’in expires direktifi Expires ve Cache-Control: max-age başlıklarını otomatik üretiyor, basit durumlar için yeterli:
expires 30d;
Ama immutable ya da must-revalidate gibi ince ayarları expires üretmiyor. Bu ikisi gerektiğinde add_header Cache-Control yazmak zorundasınız. İkisini aynı location’da birlikte kullanmayın, birini seçin.
add_header miras almıyor
Bu en çok zaman kaybettiren nginx davranışı. Bir location bloğunda add_header tanımladığınız an, üst seviyeden (server ya da http bloğundan) miras alınan add_header satırları o response için sıfırlanıyor.
server {
add_header X-Frame-Options "SAMEORIGIN" always;
location ~* \.(?:js|css)$ {
add_header Cache-Control "public, max-age=31536000, immutable" always;
}
}
Bu location bloğuna bir add_header yazdığınız an, server seviyesindeki X-Frame-Options bu response’a hiç eklenmiyor. Çözüm tek: her locationda ihtiyacınız olan tüm add_header satırlarını tekrar yazın.
always olmadan hata sayfalarında başlık kaybolur
add_header varsayılan olarak sadece 200, 301, 302, 304 gibi “başarılı” sayılan response kodlarında ekleniyor. 404 ya da 500 döndüğünde başlık gitmiyor.
add_header Cache-Control "no-store" always;
always parametresi bunu her response koduna zorluyor. Hata sayfalarında da Cache-Control görmek istiyorsanız bu parametre şart.
gzip sadece metin için
Metin tabanlı içerik gzip’le küçülüyor, zaten sıkıştırılmış içerik küçülmüyor.
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml application/xml;
gzip_min_length 1024;
PNG, JPEG, WebP ve WOFF2 zaten sıkıştırılmış formatlar. Bunları gzip_typese eklemek CPU’yu harcayıp dosyayı büyütmekten başka bir şey yapmıyor, bazen çıktı birkaç bayt daha büyük çıkıyor. gzip_types listesine sadece metin tipleri girer.
Doğrulama: curl ile kontrol edin
Konfigürasyonu deploy ettikten sonra tarayıcıya güvenmeyin, başlıkları doğrudan okuyun:
curl -I https://example.com/_astro/index.4f2a91c3.js
curl -I https://example.com/
İlk komutun çıktısında cache-control: public, max-age=31536000, immutable görmeniz gerekiyor. İkincisinde cache-control: public, max-age=0, must-revalidate. Biri diğerinin başlığını taşıyorsa location eşleşmesi yanlış yerde, muhtemelen bir regex’in kapsadığı alan beklediğinizden geniş ya da dar.
Özet
Hash’li varlıklara immutable ile bir yıl verin, HTML’e must-revalidate ile sıfır verin. Her locationda ihtiyacınız olan add_header satırlarını tekrar yazın, üstten miras almayı beklemeyin. always parametresini unutmayın, gzip’i sadece metin tiplerine açın.