IIS’te Statik İçerik Cache Yapılandırması
Üretim ortamında IIS yönetiyorsanız, statik içerik cache’lemenin ne kadar kritik olduğunu muhtemelen zaten biliyorsunuzdur. Ama bunu doğru yapılandırmak, sadece birkaç checkbox işaretlemekten çok daha fazlasını gerektiriyor. Yıllarca farklı ölçeklerde IIS kurulumları yönettikten sonra şunu söyleyebilirim: çoğu performans sorununun kökeninde ya yanlış cache yapılandırması ya da hiç yapılandırılmamış cache var.
Neden Statik İçerik Cache’i Bu Kadar Önemli?
Bir e-ticaret sitesi düşünün. Her sayfa yüklendiğinde aynı logo, aynı CSS dosyaları, aynı JavaScript kütüphaneleri tekrar tekrar sunucudan istemciye gidiyor. Binlerce eş zamanlı kullanıcıyla bunu çarpın. Hem bant genişliğiniz gereksiz yere tükeniyor hem de sunucu kaynaklarınız boşa harcıyor.
IIS’in statik içerik cache mekanizması temelde iki katmanda çalışır:
- Kernel-mode cache: İşletim sistemi çekirdeği seviyesinde, HTTP.sys tarafından yönetilen, son derece hızlı bir cache katmanı
- User-mode cache: IIS worker process seviyesinde çalışan, daha esnek ama biraz daha yavaş olan katman
Bu iki katmanı anlamadan yapılandırma yapmaya kalkmak, motoru görmeden araba tamir etmeye benzer.
HTTP.sys Kernel Cache Nasıl Çalışır?
Windows’ta HTTP istekleri doğrudan kernel’e gelir, HTTP.sys bu isteği karşılar. Eğer istek cache’de mevcutsa, istek hiçbir zaman user-mode’a, yani IIS worker process’e ulaşmaz bile. Bu, performans açısından muazzam bir avantaj sağlar.
Kernel cache’in aktif olup olmadığını kontrol etmek için:
netsh http show cachestate
Bu komut o an kernel cache’de tutulan URL’leri ve boyutlarını gösterir. Eğer çıktı boşsa ya cache boş ya da kernel cache devre dışı demektir.
Kernel cache sınırlarını görüntülemek için:
netsh http show iplisten
netsh http show servicestate
IIS Manager Üzerinden Temel Cache Yapılandırması
Önce görsel arayüzden anlayalım, sonra PowerShell ve XML konfigürasyonuna geçeceğiz.
IIS Manager’da site seçili iken Static Content modülüne gidin. Burada bulamıyorsanız, Features View’da olmayabilir, önce role service olarak yüklü olduğundan emin olun.
Ama asıl cache yönetimi için Output Caching bölümüne gitmeniz gerekiyor. Bu bölümde dosya uzantısı bazında kurallar tanımlayabilirsiniz.
Örnek senaryo: Bir kurumsal web sitesinde CSS, JS ve resim dosyaları haftalar boyunca değişmiyor ama HTML sayfaları dinamik içerik barındırıyor. Bu durumda sadece statik uzantılar için cache kuralı oluşturmak mantıklı.
Web.config ile Cache-Control Header’ları
Burada işler gerçekten ilginçleşiyor. Tarayıcı tarafında caching için HTTP header’larını doğru ayarlamak, sunucu tarafı cache kadar önemli.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge"
cacheControlMaxAge="7.00:00:00" />
</staticContent>
</system.webServer>
</configuration>
Bu yapılandırma tüm statik içerik için 7 günlük browser cache süresi belirler. Ama bu yaklaşımın bir sorunu var: tüm statik dosyalara aynı kuralı uyguluyor. Logo 7 gün cache’lenebilir ama ya sık güncellenen bir konfigürasyon JSON dosyanız varsa?
Daha granüler bir yaklaşım için bloklarını kullanın:
<configuration>
<location path="assets/images">
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge"
cacheControlMaxAge="30.00:00:00" />
</staticContent>
</system.webServer>
</location>
<location path="assets/css">
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge"
cacheControlMaxAge="7.00:00:00" />
</staticContent>
</system.webServer>
</location>
<location path="assets/js">
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge"
cacheControlMaxAge="7.00:00:00" />
</staticContent>
</system.webServer>
</location>
</configuration>
MIME Türlerine Göre Cache Kuralları
Farklı dosya türleri için farklı cache süreleri belirlemek istiyorsanız, outboundRules ile HTTP response header’larını manipüle edebilirsiniz. URL Rewrite modülü yüklü olmalı:
<system.webServer>
<rewrite>
<outboundRules>
<rule name="Cache-Control for Images">
<match serverVariable="RESPONSE_Cache_Control"
pattern=".*" />
<conditions>
<add input="{REQUEST_URI}"
pattern=".(jpg|jpeg|png|gif|ico|svg|webp)$" />
</conditions>
<action type="Rewrite"
value="public, max-age=2592000" />
</rule>
<rule name="Cache-Control for Fonts">
<match serverVariable="RESPONSE_Cache_Control"
pattern=".*" />
<conditions>
<add input="{REQUEST_URI}"
pattern=".(woff|woff2|ttf|eot)$" />
</conditions>
<action type="Rewrite"
value="public, max-age=31536000, immutable" />
</rule>
</outboundRules>
</rewrite>
</system.webServer>
Font dosyaları için immutable direktifini kullandığıma dikkat edin. Font dosyaları neredeyse hiç değişmez ve modern tarayıcılar bu direktifi gördüğünde revalidation bile yapmaz. Bu küçük detay, özellikle mobil kullanıcılar için önemli performans kazanımı sağlar.
PowerShell ile IIS Output Cache Yönetimi
Komut satırından yönetim yapmanız gerektiğinde, ya toplu değişiklik uygulayacaksınız ya da bir otomasyon scripti yazıyorsunuzdur, PowerShell vazgeçilmez oluyor.
WebAdministration modülünü import edin:
Import-Module WebAdministration
# Mevcut output cache kurallarını listele
Get-WebConfiguration -Filter "system.webServer/caching/profiles/add" -PSPath "IIS:SitesDefault Web Site"
# Yeni cache kuralı ekle - .css dosyaları için
Add-WebConfiguration -Filter "system.webServer/caching/profiles" `
-PSPath "IIS:SitesBenimSitem" `
-Value @{
extension = ".css"
policy = "CacheUntilChange"
kernelCachePolicy = "CacheUntilChange"
location = "Any"
varyByHeaders = "Accept-Encoding"
}
CacheUntilChange politikası özellikle ilgimi çeken bir seçenek. Dosya değiştiği anda cache otomatik olarak geçersiz kılınır. Deployment süreçlerinde dosyalar güncelleniyor ama cache temizlenmediği için eski içerik servis edilmeye devam ediyor, bu problemi tamamen ortadan kaldırıyor.
Kernel Cache’i PowerShell ile Yönetmek
# IIS kernel cache ayarlarını görüntüle
Get-WebConfiguration -Filter "system.webServer/serverRuntime" |
Select-Object frequentHitThreshold, frequentHitTimePeriod
# Kernel cache eşik değerlerini ayarla
Set-WebConfiguration -Filter "system.webServer/serverRuntime" `
-Value @{
frequentHitThreshold = 2
frequentHitTimePeriod = "00:00:10"
}
frequentHitThreshold değeri, bir URL’nin kernel cache’e alınması için kaç kez istek alması gerektiğini belirler. Varsayılan değer 2’dir, yani bir URL 10 saniye içinde 2 kez istek aldıysa kernel cache’e alınır. Yoğun trafik alan bir sitede bu değeri 1’e çekebilirsiniz, ama hafıza kullanımını da göz önünde bulundurun.
applicationHost.config Seviyesinde Global Cache Ayarları
Site bazlı değil de sunucu genelinde ayarlar yapmak istiyorsanız, applicationHost.config dosyasına bakmanız gerekiyor. Bu dosya genellikle C:WindowsSystem32inetsrvconfig dizininde bulunur.
Dikkat: bu dosyayı doğrudan düzenlemek riskli, her zaman önce yedek alın.
<system.webServer>
<caching enabled="true" enableKernelCache="true">
<profiles>
<add extension=".html" policy="CacheUntilChange"
kernelCachePolicy="DontCache" />
<add extension=".css" policy="CacheUntilChange"
kernelCachePolicy="CacheUntilChange" />
<add extension=".js" policy="CacheUntilChange"
kernelCachePolicy="CacheUntilChange" />
<add extension=".png" policy="CacheUntilChange"
kernelCachePolicy="CacheUntilChange" />
<add extension=".jpg" policy="CacheUntilChange"
kernelCachePolicy="CacheUntilChange" />
</profiles>
</caching>
</system.webServer>
HTML dosyalarında kernelCachePolicy="DontCache" kullandım. Sebebi şu: HTML içeriği sıkça değişebilir ve kernel cache’den çıkarmak için özel işlem gerekebilir. Dinamik HTML’i kernel cache’e almak, stale content servis etme riskini artırır.
ETag ve Last-Modified Header Yönetimi
Cache-Control header’larının yanı sıra ETag ve Last-Modified header’ları da tarayıcı caching mekanizmasının önemli parçaları.
IIS varsayılan olarak ETag üretir ama bu ETags’in formatı, yük dengeleme ortamlarında sorun çıkarabilir. Farklı sunucular aynı dosya için farklı ETag değerleri üretebilir.
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge"
cacheControlMaxAge="7.00:00:00"
setEtag="true" />
</staticContent>
</system.webServer>
Birden fazla IIS sunucusunun olduğu bir ortamda, ETags’i tamamen devre dışı bırakmak ve sadece Last-Modified + Cache-Control kombinasyonuna güvenmek daha güvenli bir yaklaşım olabilir.
Gerçek Dünya Senaryosu: CDN Arkasında IIS
Türkiye’de hizmet veren bir içerik platformu düşünelim. Cloudflare veya Akamai gibi bir CDN önünde IIS çalışıyor. Bu senaryoda cache stratejisi biraz farklılaşıyor.
CDN kendi kurallarına göre içerik cache’liyor, ama bunu doğru yapabilmesi için IIS’in doğru header’ları göndermesi şart. Özellikle Cache-Control: public directive’i çok kritik. private olarak işaretlenmiş içerik CDN tarafından cache’lenmez.
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Cache-Control" value="public, max-age=86400, s-maxage=604800" />
<add name="Vary" value="Accept-Encoding" />
</customHeaders>
</httpProtocol>
</system.webServer>
Burada s-maxage direktifi sadece CDN’ler gibi shared cache’ler için geçerli. Tarayıcı max-age değerini kullanırken, CDN s-maxage değerini kullanır. Bu şekilde CDN’de 7 gün, tarayıcıda 1 gün gibi farklı süreler ayarlayabilirsiniz. Deployment sonrasında CDN cache’ini temizleme API’si çağırırsınız ama kullanıcıların tarayıcı cache’ine müdahale edemezsiniz, bu yüzden tarayıcı süresini daha kısa tutmak mantıklı.
Cache Bozulması Sorunlarını Gidermek
En sık karşılaştığım sorun şu: deployment yapıldı, CSS değişti, ama bazı kullanıcılar eski stili görmeye devam ediyor. Sorun genellikle üç yerden kaynaklanıyor.
Birinci sebep: Dosya versiyonlama yapılmamış. Çözüm, asset URL’lerine version query string eklemek: style.css?v=2.1.4
İkinci sebep: no-cache ile no-store direktiflerinin karıştırılması. no-cache, içeriği cache’leme ama kullanmadan önce sunucuyla doğrula demektir. no-store ise hiç cache’leme demektir. Pek çok geliştirici bu ikisini karıştırıyor.
Üçüncü sebep: IIS output cache’inin temizlenmemesi. Deployment scriptlerinize şunu ekleyin:
# IIS output cache'ini temizle
& "$env:SystemRootsystem32inetsrvappcmd.exe" clear cache
# Veya belirli bir site için
& "$env:SystemRootsystem32inetsrvappcmd.exe" clear cache `
/url:"http://www.orneksite.com/assets/"
Compression ve Cache Birlikte Kullanımı
Gzip veya Brotli sıkıştırma ile cache birlikte kullanılırken dikkat edilmesi gereken bir nokta var. Vary: Accept-Encoding header’ı mutlaka olmalı, yoksa sıkıştırılmış içerik sıkıştırmayı desteklemeyen bir tarayıcıya servis edilebilir.
<system.webServer>
<urlCompression doStaticCompression="true" doDynamicCompression="false" />
<staticContent>
<clientCache cacheControlMode="UseMaxAge"
cacheControlMaxAge="7.00:00:00" />
</staticContent>
<httpProtocol>
<customHeaders>
<add name="Vary" value="Accept-Encoding" />
</customHeaders>
</httpProtocol>
</system.webServer>
IIS statik sıkıştırma etkin olduğunda, sıkıştırılmış dosyaları disk üzerinde %SystemDrive%inetpubtempIIS Temporary Compressed Files altında cache’ler. Bu dizinin boyutunu izleyin, özellikle çok sayıda büyük statik dosya varsa dolabilir.
Cache Hit Oranını İzlemek
Yapılandırmayı yaptınız, peki ne kadar etkili olduğunu nasıl ölçeceksiniz? Performance Monitor bu konuda yardımcı olur.
# PowerShell ile cache counter'larını çek
$counters = @(
"Web Service CacheCurrent Files Cached",
"Web Service CacheTotal Files Cached",
"Web Service CacheCache Hits",
"Web Service CacheCache Misses",
"Web Service CacheCache Hit %"
)
Get-Counter -Counter $counters -SampleInterval 5 -MaxSamples 12
Cache hit oranı yüzde 80’in altındaysa, cache politikanızı gözden geçirmeniz gerekiyor. Yüzde 90 üzeri iyi bir hedef, ama bu tabii ki sitenizin içerik yapısına göre değişir.
Sonuç
IIS’te statik içerik cache yapılandırması, tek seferlik ayarlayıp unutabileceğiniz bir şey değil. Sitenizin içerik yapısı değiştikçe, trafik profili evriledikçe ve deployment süreçleriniz olgunlaştıkça bu yapılandırmayı da güncellemeniz gerekiyor.
Özetlemek gerekirse en kritik noktalar şunlar:
- Kernel cache’i etkin kullanın: Özellikle yoğun trafik alan statik dosyalar için
CacheUntilChangepolitikası ile kernel cache’i açık tutun - Tarayıcı cache süreleri için içerik türüne göre ayrım yapın: Fontlar ve görseller uzun süreli, HTML kısa süreli olmalı
- CDN varsa
s-maxagedirektifini kullanın: CDN ve tarayıcı için farklı cache süreleri belirleyin - Deployment scriptlerinize cache temizleme adımı ekleyin:
appcmd clear cachekomutu hayat kurtarır - Cache hit oranını düzenli izleyin: Performance Monitor counter’larını bir monitoring sistemine bağlayın
Vary: Accept-Encodingheader’ını unutmayın: Sıkıştırma ve cache birlikte çalışıyorsa bu şart
Bu yapılandırmaları prodüksiyona uygulamadan önce mutlaka test ortamında deneyin ve her değişikliği belgeleyin. Cache sorunları, özellikle yanlış cache edilmiş içerik, üretim ortamında hızlıca fark edilir ve kullanıcı deneyimini ciddi biçimde etkiler.
