IIS Üzerinde İlk Web Sitesi Oluşturma ve Yapılandırma
Yeni bir Windows Server kurulumu yaptınızda, IIS’i açıp bir web sitesi oluşturmak kulağa basit gelebilir. Ama iş gerçeğe geldiğinde, “neden 500 Internal Server Error alıyorum?”, “Application Pool neden sürekli duruyor?”, “HTTPS’i nasıl zorunlu kılarım?” gibi sorularla boğuşmak kaçınılmaz oluyor. Bu yazıda sıfırdan bir web sitesi kurmanın tüm adımlarını, üretim ortamında karşılaşabileceğiniz gerçek senaryolarla birlikte ele alacağım.
IIS Kurulumu: Sadece Rol Eklemekten İbaret Değil
Windows Server 2019/2022 üzerinde IIS kurulumu Server Manager üzerinden yapılabilir, ama PowerShell ile yapmak hem daha hızlı hem de tekrarlanabilir. Özellikle birden fazla sunucu yönetiyorsanız, elle tıklamak yerine bir script çalıştırmak hayat kurtarır.
# Temel IIS kurulumu
Install-WindowsFeature -Name Web-Server -IncludeManagementTools
# Daha kapsamlı kurulum (ASP.NET, yönetim araçları dahil)
Install-WindowsFeature -Name Web-Server, Web-Common-Http, Web-Static-Content, `
Web-Default-Doc, Web-Dir-Browsing, Web-Http-Errors, Web-App-Dev, `
Web-Asp-Net45, Web-Net-Ext45, Web-ISAPI-Ext, Web-ISAPI-Filter, `
Web-Health, Web-Http-Logging, Web-Security, Web-Basic-Auth, `
Web-Windows-Auth, Web-Filtering, Web-Performance, Web-Stat-Compression, `
Web-Mgmt-Tools, Web-Mgmt-Console, Web-Mgmt-Compat `
-IncludeAllSubFeature -IncludeManagementTools
Kurulum bittikten sonra IIS’in ayakta olup olmadığını doğrulayın:
# IIS servis durumu
Get-Service -Name W3SVC | Select-Object Name, Status, StartType
# IIS sürümünü kontrol et
Get-ItemProperty HKLM:SOFTWAREMicrosoftInetStp | Select-Object VersionString
Şunu söyleyeyim: Kurulum sırasında hangi modülleri seçtiğiniz sonradan ciddi başınızı ağrıtabilir. Özellikle URL Rewrite modülü IIS’in standart kurulumunda gelmiyor, ama modern web uygulamalarının neredeyse tamamı bunu kullanıyor. Kurulumu şimdi yapın, sonra “neden redirect çalışmıyor” diye saatlerce uğraşmayın.
# URL Rewrite modülünü indirip kurma (Web Platform Installer olmadan)
$urlRewriteUrl = "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_en-US.msi"
Invoke-WebRequest -Uri $urlRewriteUrl -OutFile "C:Temprewrite_amd64.msi"
Start-Process msiexec.exe -Wait -ArgumentList '/I C:Temprewrite_amd64.msi /quiet'
Application Pool Yapılandırması: En Çok Atlanan Adım
Web sitesi oluşturmadan önce Application Pool’u doğru yapılandırmak gerekiyor. Çoğu sysadmin bu adımı geçiştiriyor ve sonra garip davranışlarla karşılaşıyor. Application Pool, web uygulamanızın çalıştığı izole ortam. Her siteye ayrı bir pool açmak, bir sitede çıkan sorunun diğer siteleri etkilemesini engelliyor.
# Yeni Application Pool oluşturma
New-WebAppPool -Name "MüşteriSitesi_Pool"
# Pool özelliklerini yapılandırma
Set-ItemProperty IIS:AppPoolsMüşteriSitesi_Pool -Name "managedRuntimeVersion" -Value "v4.0"
Set-ItemProperty IIS:AppPoolsMüşteriSitesi_Pool -Name "managedPipelineMode" -Value "Integrated"
Set-ItemProperty IIS:AppPoolsMüşteriSitesi_Pool -Name "startMode" -Value "AlwaysRunning"
Set-ItemProperty IIS:AppPoolsMüşteriSitesi_Pool -Name "autoStart" -Value $true
# Recycle ayarları - günlük recycle'ı kapat, bellek sınırı koy
Set-ItemProperty IIS:AppPoolsMüşteriSitesi_Pool -Name "recycling.periodicRestart.time" -Value "00:00:00"
Set-ItemProperty IIS:AppPoolsMüşteriSitesi_Pool -Name "recycling.periodicRestart.privateMemory" -Value 512000
startMode “AlwaysRunning”: Pool’u her zaman çalışır durumda tutar. Özellikle warm-up süresi uzun olan uygulamalarda ilk istek gecikmeyi önler.
managedPipelineMode “Integrated”: Modern ASP.NET uygulamaları için integrated pipeline kullanın. Eski bir uygulama taşıyorsanız ve Classic mode gerektiriyorsa, bunu değiştirin.
periodicRestart.time “00:00:00”: Zamanlanmış recycle’ı devre dışı bırakır. Gece 2’de recycle olan bir pool, aktif kullanıcıların session’larını öldürebilir.
Bir de servis hesabı meselesi var. Varsayılan olarak Application Pool “ApplicationPoolIdentity” ile çalışır, bu genellikle sorun çıkarmaz. Ama uygulamanızın ağ kaynaklarına, SQL Server’a veya dosya paylaşımlarına erişmesi gerekiyorsa, özel bir servis hesabı kullanmanız şart.
# Pool'u özel servis hesabıyla çalıştırma
$pool = Get-Item IIS:AppPoolsMüşteriSitesi_Pool
$pool.processModel.userName = "DOMAINsvc_website"
$pool.processModel.password = "GüçlüŞifre123!"
$pool.processModel.identityType = "SpecificUser"
$pool | Set-Item
Web Sitesi Oluşturma ve Temel Yapılandırma
Artık gerçek kısma geldik. Web sitesini oluşturmak için önce dizin yapısını hazırlayın:
# Dizin yapısı oluşturma
$siteRoot = "D:Websitesmusterisitesi"
New-Item -ItemType Directory -Path "$siteRootwwwroot" -Force
New-Item -ItemType Directory -Path "$siteRootlogs" -Force
New-Item -ItemType Directory -Path "$siteRootbackups" -Force
# Application Pool hesabına izin ver
$acl = Get-Acl $siteRoot
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"IIS AppPoolMüşteriSitesi_Pool", "ReadAndExecute", "ContainerInherit,ObjectInherit", "None", "Allow"
)
$acl.SetAccessRule($accessRule)
Set-Acl $siteRoot $acl
Şimdi siteyi oluşturalım:
# Web sitesi oluşturma
New-Website -Name "MüşteriSitesi" `
-PhysicalPath "D:Websitesmusterisitesiwwwroot" `
-ApplicationPool "MüşteriSitesi_Pool" `
-Port 80 `
-HostHeader "www.musterisitesi.com" `
-Force
# Ek binding ekle (www olmadan da çalışsın)
New-WebBinding -Name "MüşteriSitesi" -Protocol "http" -Port 80 -HostHeader "musterisitesi.com"
# Log dizinini özelleştir
Set-WebConfigurationProperty -pspath "IIS:SitesMüşteriSitesi" `
-filter "system.applicationHost/sites/site[@name='MüşteriSitesi']/logFile" `
-name "directory" -value "D:Websitesmusterisitesilogs"
Temel bir test dosyası oluşturun ve siteyi doğrulayın:
# Test sayfası oluştur
$testHtml = @"
<!DOCTYPE html>
<html>
<head><title>IIS Test Sayfası</title></head>
<body>
<h1>Sunucu: $env:COMPUTERNAME</h1>
<p>Tarih: $(Get-Date -Format 'dd.MM.yyyy HH:mm:ss')</p>
</body>
</html>
"@
$testHtml | Out-File -FilePath "D:Websitesmusterisitesiwwwrootindex.html" -Encoding UTF8
# HTTP isteği yaparak test et
Invoke-WebRequest -Uri "http://localhost" -Headers @{Host="www.musterisitesi.com"} |
Select-Object StatusCode, StatusDescription
HTTPS Yapılandırması: HTTP Artık Yeterli Değil
2024’te HTTP üzerinden yayın yapan bir production sitesi kabul edilemez. SSL sertifikası kurulumu ve HTTPS binding ekleme zorunlu adımlar.
Eğer bir sertifika dosyanız varsa (.pfx formatında):
# PFX sertifikasını yükle
$certPassword = ConvertTo-SecureString -String "sertifika_sifresi" -Force -AsPlainText
$cert = Import-PfxCertificate -FilePath "C:Certsmusterisitesi.pfx" `
-CertStoreLocation "Cert:LocalMachineMy" `
-Password $certPassword
# Thumbprint'i kaydet
$thumbprint = $cert.Thumbprint
Write-Host "Sertifika yüklendi. Thumbprint: $thumbprint"
# HTTPS binding ekle
New-WebBinding -Name "MüşteriSitesi" -Protocol "https" -Port 443 -HostHeader "www.musterisitesi.com" -SslFlags 1
# Sertifikayı binding'e bağla
$binding = Get-WebBinding -Name "MüşteriSitesi" -Protocol "https"
$binding.AddSslCertificate($thumbprint, "My")
HTTP’den HTTPS’e yönlendirme için web.config kullanın:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="HTTP to HTTPS Redirect" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="off" ignoreCase="true" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}"
redirectType="Permanent" />
</rule>
</rules>
</rewrite>
<httpProtocol>
<customHeaders>
<add name="Strict-Transport-Security"
value="max-age=31536000; includeSubDomains" />
<add name="X-Content-Type-Options" value="nosniff" />
<add name="X-Frame-Options" value="SAMEORIGIN" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
Logging ve Monitoring Yapılandırması
Log’ları doğru yapılandırmadan sunucu yönetmek kör uçmak gibidir. IIS’in varsayılan log formatı yeterli değil, biraz daha fazlasına ihtiyaç var.
# W3C Extended log formatı, ek alanlar
Set-WebConfigurationProperty -pspath "IIS:SitesMüşteriSitesi" `
-filter "system.applicationHost/sites/site[@name='MüşteriSitesi']/logFile" `
-name "logExtFileFlags" `
-value "Date,Time,ClientIP,UserName,ServerIP,Method,UriStem,UriQuery,HttpStatus,Win32Status,TimeTaken,ServerPort,UserAgent,Referer,HttpSubStatus"
# Log rollover: günlük
Set-WebConfigurationProperty -pspath "IIS:SitesMüşteriSitesi" `
-filter "system.applicationHost/sites/site[@name='MüşteriSitesi']/logFile" `
-name "period" -value "Daily"
# Eski logları temizleme scripti (30 günden eski)
$logPath = "D:Websitesmusterisitesilogs"
Get-ChildItem -Path $logPath -Filter "*.log" |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
Remove-Item -Force
Failed Request Tracing (FRT) aktifleştirmek, 500 hatalarının köküne inmek için paha biçilmez:
# Failed Request Tracing modülünü aktifleştir
Enable-WebRequestTracing -Name "MüşteriSitesi" -Directory "D:WebsitesmusterisitesilogsFailedReqs" -MaxLogFiles 50
# 500 hataları ve 30 saniyeden uzun istekleri yakala
Add-WebConfigurationProperty -pspath "IIS:SitesMüşteriSitesi" `
-filter "system.webServer/tracing/traceFailedRequests" `
-name "." `
-value @{
path="*"
customActionsEnabled=$false
}
Yaygın Sorunlar ve Çözümleri
403 Forbidden Hatası
En sık karşılaşılan sorunlardan biri. Genellikle iki sebebi var:
Dizin gezinme açık değil ve index dosyası yok: web.config’e ekleyin ve bir default document tanımlayın.
NTFS izin sorunu: Application Pool kimliğinin dizine okuma izni olmayabilir. Yukarıdaki ACL ayarlarını kontrol edin.
# İzinleri kontrol et
Get-Acl "D:Websitesmusterisitesiwwwroot" | Format-List
# Default document ekle
Add-WebConfigurationProperty -pspath "IIS:SitesMüşteriSitesi" `
-filter "system.webServer/defaultDocument/files" `
-name "." `
-value @{value="index.html"}
Application Pool Sürekli Duruyor
Bu genellikle uygulama seviyesinde bir hata değil, kimlik doğrulama ya da izin sorunudur. Windows Event Log bakın:
# Application Pool crash loglarını incele
Get-EventLog -LogName Application -Source "WAS" -Newest 20 |
Where-Object { $_.EntryType -eq "Error" } |
Select-Object TimeGenerated, Message |
Format-List
# Pool'u manuel başlat ve durumunu izle
Start-WebAppPool -Name "MüşteriSitesi_Pool"
Start-Sleep -Seconds 5
(Get-WebAppPoolState -Name "MüşteriSitesi_Pool").Value
500.19 – Configuration Error
web.config dosyasında sözdizimi hatası ya da izin sorunu olduğunda çıkar. Özellikle URL Rewrite kuralları yazılmış ama modül kurulmamışsa bu hatayla karşılaşırsınız.
# IIS config doğrulama
& "$env:SystemRootSystem32inetsrvappcmd.exe" list config "MüşteriSitesi" /section:system.webServer
# applicationHost.config'i doğrula
& "$env:SystemRootSystem32inetsrvappcmd.exe" list config /section:system.applicationHost
Performans Optimizasyonu
Siteyi ayağa kaldırdınız, çalışıyor, peki ya performans? Birkaç temel ayar büyük fark yaratıyor.
Statik içerik sıkıştırma ve output caching aktifleştirme:
# Statik ve dinamik sıkıştırmayı aktifleştir
Set-WebConfigurationProperty -pspath "IIS:" `
-filter "system.webServer/httpCompression" `
-name "doDynamicCompression" -value $true
Set-WebConfigurationProperty -pspath "IIS:" `
-filter "system.webServer/httpCompression" `
-name "doStaticCompression" -value $true
# Output cache ayarları
Set-WebConfigurationProperty -pspath "IIS:SitesMüşteriSitesi" `
-filter "system.webServer/caching" `
-name "enabled" -value $true
Set-WebConfigurationProperty -pspath "IIS:SitesMüşteriSitesi" `
-filter "system.webServer/caching" `
-name "enableKernelCache" -value $true
Statik dosyalar için browser cache header’ları da ekleyin:
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
<mimeMap fileExtension=".woff2" mimeType="font/woff2" />
<mimeMap fileExtension=".webp" mimeType="image/webp" />
</staticContent>
Firewall ve Güvenlik Ayarları
IIS kurulduğunda Windows Firewall kuralları otomatik eklenir, ama bunları doğrulamak iyi alışkanlık:
# IIS ile ilgili firewall kurallarını listele
Get-NetFirewallRule | Where-Object { $_.DisplayName -like "*HTTP*" -or $_.DisplayName -like "*IIS*" } |
Select-Object DisplayName, Direction, Action, Enabled
# 80 ve 443 portunu açık olduğunu doğrula
Test-NetConnection -ComputerName localhost -Port 80
Test-NetConnection -ComputerName localhost -Port 443
# Gerekirse manuel ekle
New-NetFirewallRule -DisplayName "IIS HTTP" -Direction Inbound -Protocol TCP -LocalPort 80 -Action Allow
New-NetFirewallRule -DisplayName "IIS HTTPS" -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow
IIS Manager üzerinden uzaktan yönetim için ise ayrı bir port (8172) açmanız gerekiyor; bunu yalnızca yönetim ağından erişilebilir yapın.
Sitenin Sağlığını Düzenli Kontrol Etme
Monitoring kurmadan bırakmayın. Basit bir PowerShell script bile çok iş görür:
# Site durum kontrol scripti - Task Scheduler'a ekleyin
$siteler = @("MüşteriSitesi", "DigerSite")
$alertEmail = "[email protected]"
foreach ($site in $siteler) {
$state = (Get-WebsiteState -Name $site).Value
$poolState = (Get-WebAppPoolState -Name ($site + "_Pool")).Value
if ($state -ne "Started" -or $poolState -ne "Started") {
$body = "UYARI: $site sitesi veya pool'u durdu! Site: $state, Pool: $poolState"
Send-MailMessage -To $alertEmail -Subject "IIS Uyarı: $site" `
-Body $body -SmtpServer "mail.sirket.com"
# Otomatik restart dene
Start-Website -Name $site
Start-WebAppPool -Name ($site + "_Pool")
}
}
Sonuç
IIS üzerinde web sitesi kurmanın teknik adımları bunlar, ama asıl önemli olan doğru alışkanlıkları edinmek: Her siteye ayrı Application Pool, her şeyi script ile yapıp dokümante etmek, log’ları aktif tutmak ve HTTPS’i baştan zorunlu kılmak. Özellikle Application Pool kimliği ve NTFS izinleri konusunu atlayan sysadminler, sonradan “neden çalışmıyor” sorusunun cevabını saatler harcayarak buluyor.
Production ortamında bir şeyi Manuel olarak IIS Manager üzerinden değiştirirseniz, o değişikliği mutlaka PowerShell karşılığıyla da dokümante edin. Bir yıl sonra aynı konfigürasyonu başka bir sunucuya kurmak zorunda kaldığınızda, “bu ayarı nerede yapmıştım” diye elinizden geleni yaparsınız. Script’ler hem hafızanız hem de meslektaşlarınız için en iyi dokümantasyon biçimi.
