IIS’te 404, 403 ve 500 Hata Kodlarını Giderme
Yıllar içinde IIS’in önüne ne kadar çok şey koyduğumuzu düşündüğümde, hâlâ aynı hata kodlarıyla boğuştuğumuzu görünce gülümsüyorum. 404, 403, 500 — bu üçlü IIS yöneticilerinin kabusudur. Ama şunu da söyleyeyim: bu hatalar aslında çok konuşkandır. Sadece ne dediklerini anlamayı öğrenmek gerekiyor.
Bu yazıda sizi teorik bir kılavuzla boğmayacağım. Gerçek senaryolar, gerçek komutlar ve “işte burada tıkandım” dediğinizde ne yapacağınızı anlatacağım.
IIS Hata Ayıklamasına Başlamadan Önce: Temel Hazırlık
Herhangi bir hatayı gidermeye çalışmadan önce IIS’in size düzgün hata mesajı vermesini sağlamanız gerekiyor. Varsayılan kurulumda IIS, özellikle uzak istemcilere çok az bilgi verir. Bu “güvenlik özelliği” aslında troubleshooting sürecini felç eder.
İlk işiniz Detailed Errors modunu açmak olmalı. Bunu web.config üzerinden yapabilirsiniz:
# web.config içinde system.webServer bölümüne ekleyin
# C:inetpubwwwrootSitenizinKlasoruweb.config
<configuration>
<system.webServer>
<httpErrors errorMode="Detailed" existingResponse="PassThrough" />
</system.webServer>
</configuration>
Alternatif olarak PowerShell ile tüm site için bunu açabilirsiniz:
# PowerShell (Yönetici olarak çalıştırın)
Import-Module WebAdministration
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.webServer/httpErrors" `
-name "errorMode" `
-value "Detailed"
Uyarı: Bu ayarı production ortamında sonsuza kadar açık bırakmayın. Hata ayıkladıktan sonra tekrar DetailedLocalOnly moduna alın. Aksi hâlde uygulama içi stack trace’ler dışarıya sızabilir.
IIS loglarının nerede olduğunu da bilmeniz gerekiyor. Varsayılan konum C:inetpublogsLogFilesW3SVC1 altındadır. Site ID’nize göre klasör adı değişir. Logları PowerShell ile de kontrol edebilirsiniz:
# Son 50 log satırını getir
Get-Content "C:inetpublogsLogFilesW3SVC1u_ex$(Get-Date -Format 'yyMMdd').log" -Tail 50
404 Hatalarını Anlamak ve Çözmek
404, “dosya bulunamadı” demek ama IIS dünyasında bu o kadar basit değil. IIS’in kendi içinde 404’ün onlarca alt türü var ve her biri farklı bir probleme işaret ediyor.
Alt Hata Kodlarına Dikkat Edin
IIS loglarında 404.0, 404.3, 404.7, 404.11 gibi kodlar görürsünüz. Bunların ne anlama geldiğini bilmek, problemi çözme sürenizi saatlerden dakikalara indirebilir.
- 404.0: Dosya gerçekten yok ya da yol yanlış
- 404.3: MIME türü tanımlı değil, IIS içeriği sunmayı reddediyor
- 404.7: Dosya uzantısı filtresi tarafından engellendi
- 404.11: URL çift encoding içeriyor (genellikle güvenlik filtresi)
- 404.13: İstek içeriği çok büyük
- 404.17: Statik dosya handler’ı bu isteği işleyemiyor (genellikle dinamik içerik sorunu)
Bir e-ticaret projesinde .pdf uzantılı faturalar müşterilere 404 dönüyordu. Saatlerce dosya yollarını kontrol ettim. Sorun basitti: IIS MIME listesinde application/pdf tanımlı değildi. Bunu PowerShell ile düzeltmek beş saniye sürdü:
# MIME türü ekle
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/SiteAdiniz' `
-filter "system.webServer/staticContent" `
-name "." `
-value @{fileExtension='.pdf'; mimeType='application/pdf'}
Ya da web.config üzerinden:
<system.webServer>
<staticContent>
<mimeMap fileExtension=".pdf" mimeType="application/pdf" />
<mimeMap fileExtension=".woff2" mimeType="font/woff2" />
<mimeMap fileExtension=".webmanifest" mimeType="application/manifest+json" />
</staticContent>
</system.webServer>
URL Rewrite Kaynaklı 404’ler
Modern web uygulamalarının büyük çoğunluğu URL rewrite kullanıyor. ASP.NET MVC, Angular, React uygulamalarında “sayfayı yenile, 404 al” sorunu son derece yaygın.
Single Page Application (SPA) uygulamalarında tüm routing client-side yapılır. IIS’in bunu bilmesi gerekiyor:
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="SPA Fallback" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
</conditions>
<action type="Rewrite" url="/index.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
URL Rewrite modülü kurulu değilse zaten hiçbiri çalışmaz. Kontrol etmek için:
# URL Rewrite modülü yüklü mü kontrol et
Get-WebGlobalModule | Where-Object {$_.Name -like "*Rewrite*"}
Boş çıktı geliyorsa Web Platform Installer veya doğrudan Microsoft’un sitesinden URL Rewrite 2.1 modülünü kurun.
Fiziksel Yol ve İzin Sorunları
404’lerin bir kısmı aslında izin sorunlarından kaynaklanır ama IIS bazen bunu 404 olarak maskeler. Site Physical Path’ini kontrol edin:
# Site fiziksel yolunu kontrol et
Get-Website -Name "SiteAdiniz" | Select-Object -ExpandProperty physicalPath
# Klasör gerçekten var mı
Test-Path "C:inetpubwwwrootSiteAdiniz"
403 Hatalarını Çözmek
403 “erişim yasak” dediğinde genellikle dört farklı şeyden biri olmaktadır: NTFS izinleri, IIS izinleri, IP kısıtlamaları ya da istek filtreleme. Bu sırayla kontrol etmek zaman kazandırır.
NTFS İzin Sorunları
IIS uygulama havuzları kendi kimlikleriyle çalışır. Varsayılan olarak IIS AppPoolUygulamaHavuzuAdiniz kimliğini kullanırlar. Bu hesabın ilgili klasöre okuma (ve gerekiyorsa yazma) yetkisi olması şarttır.
# Uygulama havuzu kimliğini öğren
Get-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.applicationHost/applicationPools/add[@name='DefaultAppPool']" `
-name "processModel.userName"
# Klasör izinlerini kontrol et
Get-Acl "C:inetpubwwwrootSiteAdiniz" | Format-List
# IIS_IUSRS grubuna okuma izni ver (genellikle bu yeterli)
$acl = Get-Acl "C:inetpubwwwrootSiteAdiniz"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"IIS_IUSRS", "ReadAndExecute", "ContainerInherit,ObjectInherit", "None", "Allow"
)
$acl.SetAccessRule($rule)
Set-Acl "C:inetpubwwwrootSiteAdiniz" $acl
Komut satırı ile daha hızlı çözüm arıyorsanız:
# icacls ile hızlı izin verme
icacls "C:inetpubwwwrootSiteAdiniz" /grant "IIS AppPoolDefaultAppPool:(OI)(CI)R" /T
Directory Browsing ve Default Document Sorunu
Klasöre istek geldiğinde ne gösterileceği tanımlı değilse IIS 403.14 döner. Bu çok sık karşılaştığım bir durum. Ya default document ekleyeceksiniz ya da directory browsing açacaksınız (production’da genellikle ikincisi tehlikeli):
# Default document ekle
Add-WebConfigurationProperty -pspath "IIS:SitesSiteAdiniz" `
-filter "system.webServer/defaultDocument/files" `
-name "." `
-value @{value='index.html'}
# Mevcut default document listesini görüntüle
Get-WebConfigurationProperty -pspath "IIS:SitesSiteAdiniz" `
-filter "system.webServer/defaultDocument/files" `
-name "Collection"
IP ve Domain Kısıtlamaları
Özellikle kurumsal ortamlarda IP kısıtlamaları aktif olabilir. Birileri bir IP bloğunu kara listeye almış ve siz habersizsinizdir:
# Mevcut IP kısıtlamalarını listele
Get-WebConfigurationProperty -pspath "IIS:SitesSiteAdiniz" `
-filter "system.webServer/security/ipSecurity" `
-name "Collection"
# Tüm IP kısıtlamalarını kaldır (dikkatli kullanın)
Clear-WebConfiguration -pspath "IIS:SitesSiteAdiniz" `
-filter "system.webServer/security/ipSecurity"
500 Hatalarını Derinlemesine İncelemek
500 serisi hatalar, özellikle .NET uygulamalarında en sinir bozucu olanlardır. “Sunucu hatası” deyip geçen IIS size hiçbir şey söylemez gibi gelir ama aslında söylüyor, siz sadece doğru yere bakmıyorsunuzdur.
500.19 – Web.config Okuma Hatası
Bu hatayı gördüğünüzde ilk bakacağınız yer web.config dosyasının kendisidir. Ya XML malformed, ya izin sorunu var, ya da tanımsız bir modül veya handler referans edilmiştir.
# web.config syntax kontrolü (appcmd ile)
%windir%system32inetsrvappcmd.exe list config "SiteAdiniz/" /section:system.webServer
# Alternatif: PowerShell ile hızlı XML doğrulama
try {
[xml](Get-Content "C:inetpubwwwrootSiteAdinizweb.config")
Write-Host "XML gecerli" -ForegroundColor Green
} catch {
Write-Host "XML hatasi: $($_.Exception.Message)" -ForegroundColor Red
}
500.21 ve İşleyici (Handler) Sorunları
ASP.NET Core uygulamalarında çok sık karşılaştığım bu hata, genellikle ASP.NET Core Hosting Bundle’ın kurulu olmamasından kaynaklanır.
# Yüklü .NET sürümlerini kontrol et
dotnet --list-runtimes
# IIS'te yüklü modülleri kontrol et
Get-WebGlobalModule | Where-Object {$_.Name -like "*AspNetCore*"}
ASP.NET Core modülü görünmüyorsa Hosting Bundle kurmanız gerekiyor. Kurulumdan sonra IIS’i mutlaka yeniden başlatın:
# IIS'i yeniden başlat
iisreset /restart
# Sadece belirli bir uygulama havuzunu yeniden başlat
Restart-WebAppPool -Name "SiteAdiniz"
Uygulama Düzeyindeki 500 Hatalarını Yakalamak
Uygulama kodu hata fırlatıyor ve siz stack trace görmek istiyorsunuz. Event Viewer burada en iyi dostunuzdur:
# PowerShell ile Application event log'unu filtrele
Get-EventLog -LogName Application -Source "IIS*" -Newest 20 |
Select-Object TimeGenerated, EntryType, Message |
Format-List
# ASP.NET kaynaklı hataları bul
Get-EventLog -LogName Application -Source "ASP.NET*" -Newest 10 |
Select-Object TimeGenerated, Message |
Format-List
Failed Request Tracing (FREB) ise 500 hatalarını derinlemesine incelemenin en güçlü yoludur. Birçok sysadmin bunu bilmez veya kullanmaz. Aktif etmek için:
# Failed Request Tracing'i etkinleştir
Enable-WebRequestTracing -Name "SiteAdiniz"
# 500 hatalarını izle
Add-WebConfigurationProperty -pspath "IIS:SitesSiteAdiniz" `
-filter "system.webServer/tracing/traceFailedRequests" `
-name "." `
-value @{
path='*'
}
Ya da IIS Manager’dan: Site seçin > Failed Request Tracing Rules > Add > Status Code: 500. Ardından C:inetpublogsFailedReqLogFiles altına bakın. Orada oluşan XML dosyaları tarayıcıda açıldığında tam bir istek analizi sunar.
500.0 ve Uygulama Havuzu Çökmesi
Uygulama havuzu sürekli duruyorsa şunu kontrol edin:
# Uygulama havuzu durumunu kontrol et
Get-WebAppPoolState -Name "SiteAdiniz"
# Son 30 dakikadaki uygulama havuzu olaylarını bul
Get-EventLog -LogName System -Source "WAS" -Newest 20 |
Where-Object {$_.TimeGenerated -gt (Get-Date).AddMinutes(-30)} |
Select-Object TimeGenerated, EntryType, Message |
Format-List
# Uygulama havuzunu başlat
Start-WebAppPool -Name "SiteAdiniz"
Uygulama havuzu Identity’si altında çalışan bir servis hesabının şifresi değiştiyse ve havuz güncellenmemişse sürekli kapanır. Kurumsal ortamlarda bu çok sık görülür:
# Uygulama havuzu kimlik bilgilerini güncelle
Set-WebConfigurationProperty `
-pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.applicationHost/applicationPools/add[@name='SiteAdiniz']/processModel" `
-name "userName" `
-value "DOMAINServisHesabi"
Set-WebConfigurationProperty `
-pspath 'MACHINE/WEBROOT/APPHOST' `
-filter "system.applicationHost/applicationPools/add[@name='SiteAdiniz']/processModel" `
-name "password" `
-value "YeniSifre"
Gerçek Dünya: Karma Senaryo
Bir keresinde üretim ortamında çalışan bir .NET Framework 4.7 uygulaması aniden 500 hatası vermeye başladı. Event Log’da “Access to the path is denied” yazıyordu ama hem NTFS izinleri hem de uygulama havuzu doğruydu.
Sorun şuydu: Uygulama çalışma sırasında %TEMP% klasörüne geçici dosya yazmaya çalışıyordu. Uygulama havuzu ApplicationPoolIdentity olarak çalışıyordu ve bu kimliğin temp klasörüne yazma izni yoktu.
Çözüm:
# Uygulama havuzu temp klasörünü belirle ve izin ver
$appPoolUser = "IIS AppPoolSiteAdiniz"
$tempPath = "C:WindowsTemp"
icacls $tempPath /grant "${appPoolUser}:(OI)(CI)M" /T
# Alternatif: Uygulamaya özel temp klasörü tanımla
# web.config içinde:
# <environmentVariables>
# <environmentVariable name="TEMP" value="C:inetpubtempSiteAdiniz" />
# </environmentVariables>
Hızlı Tanı: Log Analizi ile Neyin Nerede Patladığını Anlamak
IIS loglarını manuel okumak yorucu. Birkaç satır PowerShell ile son bir saatin hata dağılımını çıkartabilirsiniz:
# Son IIS log dosyasını bul ve hataları grupla
$logFile = Get-ChildItem "C:inetpublogsLogFilesW3SVC1" |
Sort-Object LastWriteTime -Descending | Select-Object -First 1
Get-Content $logFile.FullName |
Where-Object {$_ -notmatch "^#"} |
ForEach-Object {
$fields = $_ -split " "
[PSCustomObject]@{
Time = $fields[1]
Method = $fields[3]
URI = $fields[4]
StatusCode = $fields[8]
SubStatus = $fields[9]
}
} |
Where-Object {$_.StatusCode -match "^(4|5)"} |
Group-Object StatusCode |
Sort-Object Count -Descending |
Select-Object Name, Count
Bu komut size son gündeki hata kodlarının dağılımını verir. Hangi kodun kaç kez geldiğini anlık görürsünüz.
Önleyici Tedbirler: Hata Yönetimini Proaktif Hâle Getirmek
Hataları düzeltmek kadar onları erkenden fark etmek de önemli. IIS üzerinde basit bir izleme için Windows’un dahili araçlarını kullanabilirsiniz.
Uygulama havuzu her çöktüğünde e-posta atmak için:
# Scheduled Task ile havuz izleme
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" `
-Argument '-NonInteractive -Command "
$stoppedPools = Get-WebAppPoolState | Where-Object {$_.Value -eq "Stopped"}
if ($stoppedPools) {
Send-MailMessage -To [email protected] -From [email protected] `
-Subject "IIS App Pool Durdu!" `
-Body ($stoppedPools | Out-String) `
-SmtpServer smtp.sirket.com
}
"'
$trigger = New-ScheduledTaskTrigger -RepetitionInterval (New-TimeSpan -Minutes 5) -Once -At (Get-Date)
Register-ScheduledTask -TaskName "IIS-HavuzIzleme" -Action $action -Trigger $trigger -RunLevel Highest
Sonuç
IIS hata kodları, bir sistemin size sinyaller verme biçimidir. 404, 403 ve 500 hatalarının her biri kendi içinde onlarca alt kategoriye ayrılır ve her birinin çözüm yolu farklıdır. Ancak şunu fark ettim: bu hataların büyük çoğunluğu birkaç temel nedene indirgeniyor.
- İzin sorunları (NTFS, uygulama havuzu kimliği)
- Eksik modüller veya handler’lar
- Hatalı veya eksik web.config yapılandırması
- MIME türü eksiklikleri
- Uygulama kodu hataları
Bunları sistematik şekilde kontrol ederseniz, çözüme ulaşma süreniz dramatik şekilde kısalır. Loglara bakın, Event Viewer’ı kullanın, Failed Request Tracing’i ihmal etmeyin. IIS size her şeyi söylüyor, sadece doğru yerde dinlemeniz gerekiyor.
Son bir not: Production ortamında hata ayıklarken yaptığınız tüm değişiklikleri kayıt altına alın. “Bir şey deneyeyim, sonra geri alırım” yaklaşımı IIS’te çok pahalıya patlayabiliyor.
