Windows Server IIS and Web Hosting: Enterprise Web Services
Introduction
Internet Information Services (IIS) powers enterprise web applications on Windows Server. This guide covers IIS installation and configuration, application pool isolation, SSL/TLS certificate management, URL rewrite rules, load balancing with ARR, web farm deployment, security best practices, and performance optimization.
IIS Installation and Configuration
Installing IIS
# Install IIS with common features
Install-WindowsFeature -Name Web-Server, Web-Mgmt-Tools, Web-Asp-Net45, Web-Windows-Auth, Web-Url-Auth, Web-Filtering, Web-IP-Security
# Install additional features
Install-WindowsFeature -Name Web-WebSockets, Web-AppInit, Web-Http-Redirect
# Import WebAdministration module
Import-Module WebAdministration
# Verify IIS installation
Get-Service W3SVC
Get-Website
Creating Websites
# Create website
New-Website -Name "ContosoWeb" `
-PhysicalPath "C:\inetpub\wwwroot\ContosoWeb" `
-Port 80 `
-HostHeader "www.contoso.com" `
-ApplicationPool "ContosoAppPool"
# Create website with SSL
New-Website -Name "SecureWeb" `
-PhysicalPath "C:\inetpub\wwwroot\SecureWeb" `
-Port 443 `
-HostHeader "secure.contoso.com" `
-Ssl `
-ApplicationPool "SecureAppPool"
# Add additional binding
New-WebBinding -Name "ContosoWeb" -Protocol "http" -Port 80 -HostHeader "contoso.com"
# View websites
Get-Website | Select-Object Name, ID, State, PhysicalPath, Bindings
# Start/stop website
Start-Website -Name "ContosoWeb"
Stop-Website -Name "ContosoWeb"
# Remove website
Remove-Website -Name "OldSite" -Confirm:$false
Managing Virtual Directories and Applications
# Create virtual directory
New-WebVirtualDirectory -Site "ContosoWeb" -Name "Documents" -PhysicalPath "C:\SharedDocs"
# Create application
New-WebApplication -Site "ContosoWeb" -Name "API" -PhysicalPath "C:\WebApps\API" -ApplicationPool "APIAppPool"
# View virtual directories and applications
Get-WebVirtualDirectory -Site "ContosoWeb"
Get-WebApplication -Site "ContosoWeb"
Application Pool Management
Creating Application Pools
# Create application pool
New-WebAppPool -Name "ContosoAppPool"
# Configure application pool
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "managedRuntimeVersion" -Value "v4.0"
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "managedPipelineMode" -Value "Integrated"
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "startMode" -Value "AlwaysRunning"
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "enable32BitAppOnWin64" -Value $false
# Set identity
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "processModel.identityType" -Value "ApplicationPoolIdentity"
# Or use custom account
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "processModel.identityType" -Value "SpecificUser"
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "processModel.userName" -Value "CONTOSO\WebAppUser"
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "processModel.password" -Value "P@ssw0rd123!"
# View application pools
Get-IISAppPool | Select-Object Name, Status, ManagedRuntimeVersion, ManagedPipelineMode
Recycling Configuration
# Configure recycling
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "recycling.periodicRestart.time" -Value "1.05:00:00" # 29 hours
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "recycling.periodicRestart.memory" -Value 0 # Disabled
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "recycling.periodicRestart.privateMemory" -Value 2000000 # 2 GB
# Schedule recycling times
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "recycling.periodicRestart.schedule" -Value @{value="02:00:00"}
# Set idle timeout
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "processModel.idleTimeout" -Value "00:20:00" # 20 minutes
# Configure rapid-fail protection
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "failure.rapidFailProtection" -Value $true
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "failure.rapidFailProtectionMaxCrashes" -Value 5
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "failure.rapidFailProtectionInterval" -Value "00:05:00"
# Recycle application pool
Restart-WebAppPool -Name "ContosoAppPool"
SSL/TLS Certificate Management
Installing SSL Certificates
# Generate Certificate Signing Request (CSR)
$cert = New-SelfSignedCertificate -DnsName "www.contoso.com" `
-CertStoreLocation "cert:\LocalMachine\My" `
-KeyLength 2048 `
-KeyExportPolicy Exportable `
-KeyUsage DigitalSignature, KeyEncipherment `
-NotAfter (Get-Date).AddYears(2)
# Export CSR (for production, get certificate from CA)
# For self-signed testing:
$certPath = "cert:\LocalMachine\My\$($cert.Thumbprint)"
# Bind certificate to website
New-WebBinding -Name "ContosoWeb" -Protocol "https" -Port 443 -HostHeader "www.contoso.com" -SslFlags 1
$cert = Get-ChildItem -Path "cert:\LocalMachine\My" | Where-Object { $_.Subject -like "*contoso.com*" }
Get-Item -Path "IIS:\SslBindings\0.0.0.0!443" -ErrorAction SilentlyContinue | Remove-Item
New-Item -Path "IIS:\SslBindings\0.0.0.0!443" -Value $cert
# Bind with SNI (Server Name Indication)
New-WebBinding -Name "ContosoWeb" -Protocol "https" -Port 443 -HostHeader "www.contoso.com" -SslFlags 1 # SNI
$cert | New-Item "IIS:\SslBindings\!443!www.contoso.com"
# View SSL bindings
Get-WebBinding -Name "ContosoWeb" -Protocol "https"
TLS Configuration
# Disable TLS 1.0 and 1.1 (enable only TLS 1.2 and 1.3)
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -Force
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -Name 'Enabled' -Value 0 -PropertyType 'DWord'
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -Name 'DisabledByDefault' -Value 1 -PropertyType 'DWord'
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -Force
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -Name 'Enabled' -Value 0 -PropertyType 'DWord'
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -Name 'DisabledByDefault' -Value 1 -PropertyType 'DWord'
# Enable TLS 1.2
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -Force
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -Name 'Enabled' -Value 1 -PropertyType 'DWord'
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -Name 'DisabledByDefault' -Value 0 -PropertyType 'DWord'
# Configure HSTS (HTTP Strict Transport Security)
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" -Filter "system.applicationHost/sites/site[@name='ContosoWeb']/hsts" -Name "." -Value @{enabled=$true;max-age=31536000;includeSubDomains=$true}
URL Rewrite Module
Installing URL Rewrite
# Download and install URL Rewrite module
# https://www.iis.net/downloads/microsoft/url-rewrite
# Or use Web Platform Installer
# Install-Module -Name WebPlatformInstaller
# Install-Package -Name UrlRewrite2
Creating Rewrite Rules
# HTTP to HTTPS redirect
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/rewrite/rules" `
-Name "." `
-Value @{name='HTTP to HTTPS';stopProcessing='True'}
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/rewrite/rules/rule[@name='HTTP to HTTPS']/match" `
-Name "url" -Value "(.*)"
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/rewrite/rules/rule[@name='HTTP to HTTPS']/conditions" `
-Name "." `
-Value @{input='{HTTPS}';pattern='off'}
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/rewrite/rules/rule[@name='HTTP to HTTPS']/action" `
-Name "type" -Value "Redirect"
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/rewrite/rules/rule[@name='HTTP to HTTPS']/action" `
-Name "url" -Value "https://{HTTP_HOST}/{R:1}"
# Canonical hostname (www to non-www)
# Example web.config snippet:
$rewriteConfig = @"
<rewrite>
<rules>
<rule name="Canonical Hostname" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^www\.contoso\.com$" />
</conditions>
<action type="Redirect" url="https://contoso.com/{R:1}" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
"@
Load Balancing with ARR
Installing Application Request Routing
# Download and install ARR
# https://www.iis.net/downloads/microsoft/application-request-routing
# Enable proxy mode
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" -Filter "system.webServer/proxy" -Name "enabled" -Value $true
Creating Server Farm
# Server farm configuration requires IIS Manager or web.config editing
# Example: Configure server farm in applicationHost.config
$serverFarmConfig = @"
<webFarms>
<webFarm name="ContosoFarm" enabled="true">
<server address="web01.contoso.com" enabled="true" />
<server address="web02.contoso.com" enabled="true" />
<applicationRequestRouting>
<affinity useCookie="true" cookieName="ARRAffinity" />
<loadBalance algorithm="WeightedRoundRobin" />
<healthCheck url="http://localhost/health" interval="00:00:30" />
</applicationRequestRouting>
</webFarm>
</webFarms>
"@
# URL rewrite rule to route to farm
$farmRouting = @"
<rule name="ARR_ContosoFarm_loadbalance" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<action type="Rewrite" url="http://ContosoFarm/{R:0}" />
</rule>
"@
Health Monitoring
# Health check configuration
# Create health endpoint in application (e.g., /health)
# Returns HTTP 200 when healthy
# Monitor server farm health
# Get-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" -Filter "webFarms/webFarm[@name='ContosoFarm']/server" | Select-Object address, enabled, state
Web Farm Configuration
Shared Configuration
# Export IIS configuration
Export-IISConfiguration -PhysicalPath "\\fileserver\IISConfig" `
-DontExportKeys $false `
-Force
# On additional web servers, import configuration
Enable-IISSharedConfig -PhysicalPath "\\fileserver\IISConfig" `
-KeyEncryptionPassword (ConvertTo-SecureString "P@ssw0rd123!" -AsPlainText -Force) `
-Force
# View shared config status
Get-IISSharedConfig
Content Synchronization
# Synchronize content using Robocopy
$syncScript = @"
# Sync web content from primary to secondary servers
`$source = '\\web01\C$\inetpub\wwwroot\ContosoWeb'
`$destinations = @('\\web02\C$\inetpub\wwwroot\ContosoWeb', '\\web03\C$\inetpub\wwwroot\ContosoWeb')
foreach (`$dest in `$destinations) {
robocopy `$source `$dest /MIR /R:3 /W:5 /MT:8 /LOG:C:\Logs\Sync.log
}
"@
$syncScript | Out-File "C:\Scripts\SyncContent.ps1"
# Schedule content sync
$action = New-ScheduledTaskAction -Execute 'PowerShell.exe' `
-Argument '-ExecutionPolicy Bypass -File C:\Scripts\SyncContent.ps1'
$trigger = New-ScheduledTaskTrigger -Daily -At 3am
Register-ScheduledTask -TaskName "IIS Content Sync" -Action $action -Trigger $trigger -User "SYSTEM"
Security Best Practices
Request Filtering
# Configure request filtering
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/security/requestFiltering" `
-Name "allowDoubleEscaping" -Value $false
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/security/requestFiltering" `
-Name "allowHighBitCharacters" -Value $false
# Block file extensions
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/security/requestFiltering/fileExtensions" `
-Name "." -Value @{fileExtension='.config';allowed=$false}
# Block dangerous strings
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/security/requestFiltering/denyUrlSequences" `
-Name "." -Value @{sequence='..'}
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/security/requestFiltering/denyUrlSequences" `
-Name "." -Value @{sequence='<script'}
IP Restrictions
# Allow only specific IP addresses
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/security/ipSecurity" `
-Name "allowUnlisted" -Value $false
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/security/ipSecurity" `
-Name "." -Value @{ipAddress='192.168.1.0';subnetMask='255.255.255.0';allowed=$true}
# Block specific IP
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/security/ipSecurity" `
-Name "." -Value @{ipAddress='10.0.0.50';allowed=$false}
Authentication Configuration
# Disable anonymous authentication
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/security/authentication/anonymousAuthentication" `
-Name "enabled" -Value $false
# Enable Windows authentication
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/security/authentication/windowsAuthentication" `
-Name "enabled" -Value $true
# Configure authorization
Add-WebConfiguration -Filter "/system.webServer/security/authorization" `
-Value @{accessType='Allow';roles='CONTOSO\Web Users'} `
-PSPath "IIS:\" -Location "ContosoWeb"
Remove-WebConfiguration -Filter "/system.webServer/security/authorization/add[@users='*']" `
-PSPath "IIS:\" -Location "ContosoWeb"
Performance Optimization
Static Compression
# Enable static compression
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
-Filter "system.webServer/httpCompression" `
-Name "staticCompressionEnableCpuUsage" -Value 90
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
-Filter "system.webServer/httpCompression/scheme[@name='gzip']" `
-Name "staticCompressionLevel" -Value 9
# Add file types to compress
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
-Filter "system.webServer/httpCompression/staticTypes" `
-Name "." -Value @{mimeType='application/json';enabled=$true}
Output Caching
# Enable output caching
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/caching" `
-Name "enabled" -Value $true
# Add cache rule
Add-WebConfiguration -Filter "/system.webServer/caching/profiles" `
-Value @{extension='.html';policy='CacheUntilChange';kernelCachePolicy='CacheUntilChange'} `
-PSPath "IIS:\" -Location "ContosoWeb"
# Set cache duration
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/staticContent/clientCache" `
-Name "cacheControlMode" -Value "UseMaxAge"
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/staticContent/clientCache" `
-Name "cacheControlMaxAge" -Value "7.00:00:00" # 7 days
HTTP/2 Configuration
# Enable HTTP/2 (enabled by default on Windows Server 2016+)
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
-Filter "system.applicationHost/sites/site[@name='ContosoWeb']/bindings/binding[@protocol='https']" `
-Name "sslFlags" -Value 0 # HTTP/2 requires SNI
# Verify HTTP/2 is enabled
Get-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
-Filter "system.webServer/serverRuntime" `
-Name "enabled"
Application Initialization
# Enable application initialization
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "startMode" -Value "AlwaysRunning"
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/applicationInitialization" `
-Name "doAppInitAfterRestart" -Value $true
# Add initialization page
Add-WebConfiguration -Filter "/system.webServer/applicationInitialization/add" `
-Value @{initializationPage='/warmup.aspx'} `
-PSPath "IIS:\" -Location "ContosoWeb"
Monitoring IIS
IIS Logs
# Configure logging
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.webServer/httpLogging" `
-Name "dontLog" -Value $false
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
-Filter "system.applicationHost/sites/site[@name='ContosoWeb']/logFile" `
-Name "directory" -Value "D:\IISLogs"
# View recent errors
Get-Content "C:\inetpub\logs\LogFiles\W3SVC1\*.log" -Tail 50 | Where-Object { $_ -like '*500*' }
Performance Counters
# Monitor IIS performance counters
$counters = @(
'\Web Service(_Total)\Current Connections'
'\Web Service(_Total)\Bytes Total/sec'
'\ASP.NET Applications(__Total__)\Requests/Sec'
'\ASP.NET Applications(__Total__)\Request Execution Time'
'\Process(w3wp)\% Processor Time'
'\Process(w3wp)\Private Bytes'
)
Get-Counter -Counter $counters -SampleInterval 5 -MaxSamples 10 |
ForEach-Object { $_.CounterSamples | Format-Table -AutoSize }
Key Takeaways
- IIS provides enterprise web hosting on Windows Server
- Application pools isolate applications for security and stability
- SSL/TLS certificates secure web traffic with HTTPS
- URL Rewrite handles redirects and URL normalization
- ARR delivers load balancing across web servers
- Web farms share configuration and content
- Security features include request filtering and IP restrictions
- Performance optimization includes compression, caching, and HTTP/2
Next Steps
- Deploy IIS with application pool isolation
- Configure SSL certificates for HTTPS
- Implement URL rewrite rules
- Set up load balancing with ARR
- Configure web farm with shared configuration
- Harden IIS security settings
- Optimize performance with compression and caching
Additional Resources
Host. Secure. Scale. Optimize.