Ana içeriğe geç

Browser Cache: htaccess ile Önbellekleme Ayarları

Browser Cache: htaccess ile Önbellekleme Ayarları - Teknik SEO Rehberi

Bir kullanıcı siteye ikinci kez geldiğinde tarayıcı, daha önce indirdiği statik varlıkları sunucudan tekrar istemek yerine tarayıcı önbelleğinden (browser cache) yükleyebilir. Bu davranış kendiliğinden gerçekleşmez; tarayıcının hangi kaynağı ne kadar süre saklayacağını bilmesi için sunucunun bunu HTTP başlıklarıyla bildirmesi gerekir. Apache tabanlı sunucularda bu bildirim .htaccess üzerinden yapılır.

Yapılandırma eksik kaldığında tarayıcı her seferinde koşullu istek gönderir, sunucu 304 Not Modified ya da 200 OK yanıtı verir ve her istek ayrı bir bağlantı maliyeti taşır. Yapılandırma hatalı yazıldığında ise güncellenen içerik eski haliyle önbellekte kalır.

mod_expires ile Önbellekleme Süresini Tanımlamak

Apache'de browser cache davranışını belirleyen iki modül vardır: mod_expires ve mod_headers. mod_expires, dosya türlerine göre Expires başlığı üretir ve tarayıcının bir kaynağı ne zamana kadar geçerli kabul edeceğini bildirir. Modülün .htaccess direktiflerini işleyebilmesi için sunucu yapılandırmasında etkin olması gerekir; yalnızca .htaccess'e kural yazmak yetmez.

Kural yazıldı, kaydedildi. Tarayıcı hâlâ sunucuya istek atıyor. ExpiresActive On satırı eklenmeden tüm ExpiresByType direktifleri işlenmez; Apache hiçbir uyarı vermeden kuralları atlar, sessizce devam eder, ve bu davranış özellikle yeni bir sunucu yapılandırmasında saatlerce tanılamayı uzatan bir parse hatası kadar sinir bozucu olabilir. Bu satır bloğun en başına gelmeli, ardından MIME türü bazlı süreler tanımlanmalıdır.

<IfModule mod_expires.c>
  ExpiresActive On

  # Görseller
  ExpiresByType image/jpeg    "access plus 1 year"
  ExpiresByType image/png     "access plus 1 year"
  ExpiresByType image/webp    "access plus 1 year"
  ExpiresByType image/svg+xml "access plus 1 year"
  ExpiresByType image/gif     "access plus 1 year"
  ExpiresByType image/x-icon  "access plus 1 year"

  # Stil ve betik dosyaları
  ExpiresByType text/css                 "access plus 1 year"
  ExpiresByType application/javascript   "access plus 1 year"
  ExpiresByType text/javascript          "access plus 1 year"

  # Fontlar
  ExpiresByType font/woff2  "access plus 1 year"
  ExpiresByType font/woff   "access plus 1 year"
  ExpiresByType font/ttf    "access plus 1 year"

  # HTML ve dinamik içerik
  ExpiresByType text/html        "access plus 0 seconds"
  ExpiresByType application/json "access plus 0 seconds"
</IfModule>

Görseller ve statik varlıklar için 1 yıllık süre RFC 2616'da önerilen üst sınıra karşılık gelir; daha uzun değer yazılsa da tarayıcılar 1 yılı aşan değerleri 1 yıl olarak yorumlar. HTML için sıfır saniye, her istekte sunucu doğrulamasını zorunlu kılar ve sayfa içeriğinin güncel kalmasını garanti eder. Belirtilen MIME türleri tam olarak eşleşmelidir; image/jpg yerine image/jpeg yazılmalıdır, aksi hâlde JPEG dosyaları için kural işlenmez.

Cache-Control ve Expires Başlıkları Arasındaki Ayrım

HTTP/1.0'dan gelen Expires başlığı mutlak bir tarih değeri taşır: "Bu kaynak 26 Nisan 2027 tarihine kadar geçerlidir." Sunucu ile tarayıcının sistem saatleri arasında fark varsa bu tarih yanlış hesaplanır. HTTP/1.1 ile gelen Cache-Control: max-age ise saniye cinsinden göreli bir süre bildirir ve saat uyumsuzluğundan etkilenmez.

Aynı yanıtta her ikisi de bulunuyorsa tarayıcı Cache-Control'ü önceliklendirir ve Expires değerini yok sayar. Bu iki direktifi birlikte yazmak gerekmez; geriye uyumluluk için yalnızca HTTP/1.0 istemcilerini desteklemeniz gerekiyorsa birlikte kullanmak anlamlıdır, aksi hâlde Cache-Control tek başına yeterlidir.

<IfModule mod_headers.c>
  <FilesMatch "\.(ico|jpg|jpeg|png|webp|gif|svg|woff2|woff|ttf|css|js)$">
    Header set Cache-Control "public, max-age=31536000, immutable"
  </FilesMatch>

  <FilesMatch "\.(html|htm)$">
    Header set Cache-Control "no-cache, no-store, must-revalidate"
    Header set Pragma "no-cache"
    Header set Expires "0"
  </FilesMatch>
</IfModule>

immutable direktifi, kaynak önbellek süresi dolmadan değişmeyeceğini tarayıcıya bildirir. Firefox ve Chrome bu direktifle önbellekteki dosya için koşullu istek göndermez; zorunlu yenileme (hard refresh) yapıldığında bile. CSS ve JS dosyaları için versiyonlama uygulandığında immutable gereksiz sunucu isteklerini ortadan kaldırır. Versiyonlama yoksa bu direktifi yazmak geri tepebilir.

no-cache ve no-store arasındaki fark gözden kaçar. no-cache önbelleklemeyi yasaklamaz; kaynağı saklar ama her kullanımdan önce sunucudan doğrulama ister. no-store kaynağın hiç saklanmamasını zorunlu kılar. Hassas kullanıcı verisi içeren sayfalarda no-store zorunludur; genel HTML sayfaları için no-cache yeterlidir.

Dosya Türüne Göre Önbellekleme Süresini Seçmek

Tüm dosya türlerine aynı önbellekleme süresini uygulamak hem performans açısından sorun yaratır hem de güncelleme döngüsünü kısmen kontrol dışına iter. Her dosya türünün güncelleme sıklığı farklıdır ve bu fark, süre belirlerken birincil değişken olmalıdır.

Statik varlıklar güncelleme sıklığına göre üç gruba ayrılır.

Değişmez kaynaklar için standart süre 1 yıldır. CSS, JS, font ve görseller, URL'leri değişmediği sürece bu kategoriye girer. Versiyonlama uygulandığında URL değişeceğinden tarayıcı yeni dosyayı indirmek zorunda kalır; URL sabit tutulduğunda ise önbellekteki eski sürüm servis edilmeye devam eder ve bu durum kullanıcının güncellemeyi hiç görmemesi anlamına gelir.

Nadiren değişen kaynaklar için 1 ay (2.592.000 saniye) ya da 1 hafta (604.800 saniye) tercih edilebilir. manifest.json, robots.txt ve SVG ikonlar bu gruba girer. Yakın vadede içerik değişimi planlanıyorsa kısa süre daha güvenlidir; değişim belirsizse orta süre makul bir denge sağlar.

Sık değişen kaynaklar için önbellekleme devre dışı bırakılmalıdır. Dinamik HTML sayfaları, API yanıtları ve RSS feed dosyaları bu gruptadır. A/B testi veya kişiselleştirme kullanan sayfalar da bu kategoriye dahil edilmeli; aksi hâlde kullanıcı segmentine göre farklılaşması gereken içerik aynı önbellekten servis edilir ve test sonuçları bozulur.

Cache Busting: Önbelleği Geçersiz Kılmanın Yöntemleri

1 yıllık önbellekleme süresi önemli bir performans kazancı sağlar. Aynı süre, güncellenen bir CSS dosyasının 12 ay boyunca eski haliyle servis edilmesi anlamına da gelir.

Bu çelişki, dosya adına sürüm bilgisi ya da içerik hash'i eklenerek çözülür. URL değiştiğinde tarayıcı önbellekte eşleşen kayıt bulamaz ve dosyayı yeniden indirir. Yaygın biçimler şunlardır:

style.css?v=1.4.2
style.1a2b3c4d.css
style-20260415.css

Sorgu parametresi yöntemi (?v=) bazı CDN'lerde ve proxy sunucularında önbelleği geçersiz kılmaz; çünkü ara katmanlar sorgu dizesini yok sayar. Dosya adına hash veya tarih eklenmesi daha güvenilir sonuç verir. Yapım süreci (build pipeline) yoksa ve değişiklikler manuel yapılıyorsa sorgu parametresi yöntemi pratik bir başlangıç noktasıdır.

Versiyonlama uygulanmadan max-age=31536000 ve immutable kombinasyonu geri dönüşü olmayan bir soruna kapı aralar: kullanıcılar güncellenmiş içeriği tarayıcıyı zorla yenileyene kadar göremez ve zorunlu yenileme son kullanıcıdan beklenemez. CSS ve JS dosyaları için boyut küçültme işlemi yapılırken versiyonlama stratejisi de aynı anda değerlendirilmeli; sıkıştırılmış versiyonun adı orijinalden farklıysa önbellek güncellemesi zaten otomatik gerçekleşir.

Hatalı Yapılandırmanın Belirtileri ve Doğrulama Yöntemi

Kural doğru yazılmıştır; ama işleneceği katman devre dışıdır.

İlk kontrol noktası AllowOverride ayarıdır. Apache sunucusunda AllowOverride None yapılandırması, .htaccess dosyalarının işlenmesini tamamen devre dışı bırakır; bu ayar etkinken yazılan her direktif sessizce görmezden gelinir ve Apache hiçbir hata vermez. Sunucu erişiminiz varsa httpd.conf ya da vhost yapılandırmasında AllowOverride All veya en azından AllowOverride FileInfo etkin olmalıdır.

Doğrulama için tarayıcı DevTools'un Network sekmesini açın, sayfayı yenileyin, herhangi bir statik dosyaya tıklayın ve Response Headers bölümünü inceleyin. Cache-Control: public, max-age=31536000 görünüyorsa yapılandırma işlenmiştir. Başlık yoksa ya da Cache-Control: no-cache dönüyorsa sunucu direktifi atlamıştır. Ayrı bir modül veya CDN katmanı farklı bir değer yazıyorsa .htaccess kuralı etkisizleşmiş olabilir; bu durumda CDN önbellek ayarları ayrıca yapılandırılmalıdır.

Cloudflare veya benzer bir CDN arkasında çalışan sunucularda bir sorunla daha karşılaşılır: .htaccess'te doğru başlık yazılmış, tarayıcı DevTools'ta da gözüküyor, ancak tekrar eden ziyaretçiler hâlâ beklenenden yavaş yükleme yaşıyor. Kayıt şöyle olabilir: CDN edge sunucusu dosyayı önbelleğe alıyor, ama tarayıcı her seferinde CDN'e koşullu istek gönderiyor. Bu durumda s-maxage direktifi CDN süresini, max-age ise tarayıcı süresini ayrı ayrı kontrol eder; iki değer aynı olmak zorunda değildir.

UTF-8 BOM karakteri de .htaccess'in başarısız olmasına neden olur. Bazı metin editörleri dosyayı BOM ile kaydeder; Apache bu durumda dosyanın başındaki tanınmayan karakteri okuyunca yapılandırmayı geçersiz sayar ve 500 hatası verebilir. .htaccess dosyasını her zaman UTF-8 (BOM'suz) olarak kaydedin. Yapılandırmayı doğrudan üretmek için kullanabileceğiniz bir araç, hem sözdizimini hem de karakter kodlamasını otomatik olarak doğru biçimde çıkarır.

Önbellekleme yapılandırması bir kez yazılıp unutulan bir ayar değildir. Dosya güncelleme sıklığı arttıkça, versiyonlama stratejisi değiştikçe ve CDN katmanı eklenip çıkarıldıkça gözden geçirilmesi gerekir. HTTP yanıt başlıklarını düzenli aralıklarla kontrol etmek, sessizce geçersizleşmiş kuralları erkenden tespit etmenin en kısa yoludur. CDN katmanı değiştiğinde ya da versiyonlama stratejisi güncellendiğinde .htaccess kuralları sessizce geçersizleşebilir; her kaynağın yanıt başlıklarını ve yükleme sürelerini ayrı ayrı görmek bu geçersizleşmeyi erkenden yakalar.