How to Configure IIS Web Server on Windows Server 2019
Internet Information Services (IIS) 10 on Windows Server 2019 is a full-featured, modular web server platform supporting HTTP, HTTPS, FTP, FTPS, and WebDAV. IIS powers millions of websites and web applications worldwide and integrates tightly with the .NET framework, ASP.NET Core, PHP, and Node.js via FastCGI. This guide covers installing and configuring IIS 10 for hosting websites and web applications, including SSL/TLS configuration, application pools, security hardening, and performance tuning.
Installing IIS
Install IIS with the most commonly used features using PowerShell. Installing only the features you need reduces the attack surface:
# Install IIS with common features and management tools
Install-WindowsFeature -Name Web-Server `
-IncludeManagementTools `
-IncludeAllSubFeature
# For a more targeted installation with specific features:
Install-WindowsFeature `
-Name Web-WebServer, `
Web-Common-Http, `
Web-Default-Doc, `
Web-Dir-Browsing, `
Web-Http-Errors, `
Web-Static-Content, `
Web-Http-Redirect, `
Web-Health, `
Web-Http-Logging, `
Web-Custom-Logging, `
Web-Log-Libraries, `
Web-Request-Monitor, `
Web-Http-Tracing, `
Web-Performance, `
Web-Stat-Compression, `
Web-Dyn-Compression, `
Web-Security, `
Web-Filtering, `
Web-Basic-Auth, `
Web-Windows-Auth, `
Web-App-Dev, `
Web-Net-Ext45, `
Web-Asp-Net45, `
Web-ISAPI-Ext, `
Web-ISAPI-Filter, `
Web-Mgmt-Console, `
Web-Mgmt-Tools `
-IncludeManagementTools
# Verify IIS is running
Get-Service W3SVC
Get-WebSite
Creating and Managing Application Pools
Application pools isolate web applications from each other. Each application pool runs under its own worker process (w3wp.exe) with its own identity. If one application crashes or is compromised, it cannot affect applications in other pools:
Import-Module WebAdministration
# Create a new application pool
New-WebAppPool -Name "MyApp-Pool"
# Configure the application pool
Set-ItemProperty "IIS:AppPoolsMyApp-Pool" -Name "processModel.identityType" -Value "ApplicationPoolIdentity"
Set-ItemProperty "IIS:AppPoolsMyApp-Pool" -Name "managedRuntimeVersion" -Value "v4.0"
Set-ItemProperty "IIS:AppPoolsMyApp-Pool" -Name "managedPipelineMode" -Value "Integrated"
# Enable automatic recycling every 1740 minutes (29 hours, offset from midnight)
Set-ItemProperty "IIS:AppPoolsMyApp-Pool" -Name "recycling.periodicRestart.time" -Value "00:29:00:00"
# Set maximum worker processes (Web Garden)
Set-ItemProperty "IIS:AppPoolsMyApp-Pool" -Name "processModel.maxProcesses" -Value 1
# Configure idle timeout (minutes before worker process shuts down if no requests)
Set-ItemProperty "IIS:AppPoolsMyApp-Pool" -Name "processModel.idleTimeout" -Value "00:20:00"
# Set maximum queue length
Set-ItemProperty "IIS:AppPoolsMyApp-Pool" -Name "queueLength" -Value 1000
# List all application pools
Get-WebAppPool
Creating Websites
Create a new website with its own directory, application pool, and bindings. Each website listens on a specific IP/port/hostname combination:
# Create the web root directory
New-Item -Path "C:inetpubsitesmywebsite" -ItemType Directory
# Set permissions on the web directory for the app pool identity
$acl = Get-Acl "C:inetpubsitesmywebsite"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"IIS AppPoolMyApp-Pool", "ReadAndExecute", "ContainerInherit,ObjectInherit", "None", "Allow"
)
$acl.SetAccessRule($rule)
Set-Acl "C:inetpubsitesmywebsite" $acl
# Create the website
New-Website `
-Name "mywebsite" `
-PhysicalPath "C:inetpubsitesmywebsite" `
-ApplicationPool "MyApp-Pool" `
-Port 80 `
-HostHeader "www.example.com" `
-IPAddress "*"
# Add an HTTPS binding (after importing the certificate)
New-WebBinding `
-Name "mywebsite" `
-Protocol "https" `
-Port 443 `
-IPAddress "*" `
-HostHeader "www.example.com" `
-SslFlags 1 # SNI enabled
# Assign the SSL certificate to the HTTPS binding
$cert = Get-ChildItem Cert:LocalMachineMy | Where-Object {$_.Subject -like "*example.com*"}
$binding = Get-WebBinding -Name "mywebsite" -Protocol "https"
$binding.AddSslCertificate($cert.Thumbprint, "My")
Configuring SSL/TLS and HTTPS
Securing web traffic with TLS is mandatory for any production website. Configure IIS to use only modern TLS versions and strong cipher suites. Windows Server 2019 supports TLS 1.3 natively:
# Disable SSL 2.0 and SSL 3.0 (insecure legacy protocols)
$protocols = @("SSL 2.0", "SSL 3.0", "TLS 1.0", "TLS 1.1")
foreach ($protocol in $protocols) {
$regPath = "HKLM:SYSTEMCurrentControlSetControlSecurityProvidersSCHANNELProtocols$protocolServer"
New-Item -Path $regPath -Force | Out-Null
Set-ItemProperty -Path $regPath -Name "Enabled" -Value 0 -Type DWord
Set-ItemProperty -Path $regPath -Name "DisabledByDefault" -Value 1 -Type DWord
}
# Enable TLS 1.2 and TLS 1.3
$enabledProtocols = @("TLS 1.2", "TLS 1.3")
foreach ($protocol in $enabledProtocols) {
$regPath = "HKLM:SYSTEMCurrentControlSetControlSecurityProvidersSCHANNELProtocols$protocolServer"
New-Item -Path $regPath -Force | Out-Null
Set-ItemProperty -Path $regPath -Name "Enabled" -Value 1 -Type DWord
Set-ItemProperty -Path $regPath -Name "DisabledByDefault" -Value 0 -Type DWord
}
# Configure HTTP Strict Transport Security (HSTS) via IIS URL rewrite
# Install URL Rewrite module first, then add to web.config or via PowerShell
# Add HSTS header via IIS HTTP response headers:
Set-WebConfigurationProperty `
-Filter "system.webServer/httpProtocol/customHeaders" `
-PSPath "IIS:Sitesmywebsite" `
-Name "." `
-Value @{name="Strict-Transport-Security"; value="max-age=31536000; includeSubDomains"}
Configuring Request Filtering and Security
IIS Request Filtering is the first line of defense against malicious requests. Configure it to block known attack vectors:
# Set maximum URL length (prevent buffer overflow attacks)
Set-WebConfigurationProperty `
-Filter "system.webServer/security/requestFiltering/requestLimits" `
-PSPath "IIS:Sitesmywebsite" `
-Name "maxUrl" -Value 2048
# Set maximum query string length
Set-WebConfigurationProperty `
-Filter "system.webServer/security/requestFiltering/requestLimits" `
-PSPath "IIS:Sitesmywebsite" `
-Name "maxQueryString" -Value 2048
# Set maximum request body (file upload limit: 30 MB)
Set-WebConfigurationProperty `
-Filter "system.webServer/security/requestFiltering/requestLimits" `
-PSPath "IIS:Sitesmywebsite" `
-Name "maxAllowedContentLength" -Value 31457280
# Block dangerous file extensions
Add-WebConfigurationProperty `
-Filter "system.webServer/security/requestFiltering/fileExtensions" `
-PSPath "IIS:Sitesmywebsite" `
-Name "." `
-Value @{fileExtension=".exe"; allowed="false"}
# Remove IIS version from HTTP headers (security through obscurity)
Set-WebConfigurationProperty `
-Filter "system.webServer/security/requestFiltering" `
-PSPath "IIS:" `
-Name "removeServerHeader" -Value $true
# Remove X-Powered-By header
Remove-WebConfigurationProperty `
-Filter "system.webServer/httpProtocol/customHeaders" `
-PSPath "IIS:Sitesmywebsite" `
-Name "." `
-AtElement @{name="X-Powered-By"}
Configuring IIS Logging
IIS logging records all web requests and is essential for security analysis and troubleshooting. Configure enhanced logging with additional fields:
# Configure logging for a website
Set-WebConfigurationProperty `
-Filter "system.applicationHost/sites/site[@name='mywebsite']/logFile" `
-PSPath "IIS:" `
-Name "directory" -Value "D:LogsIIS"
Set-WebConfigurationProperty `
-Filter "system.applicationHost/sites/site[@name='mywebsite']/logFile" `
-PSPath "IIS:" `
-Name "period" -Value "Daily"
# Enable W3C extended logging with additional fields
Set-WebConfigurationProperty `
-Filter "system.applicationHost/sites/site[@name='mywebsite']/logFile" `
-PSPath "IIS:" `
-Name "logExtFileFlags" `
-Value "Date,Time,ClientIP,UserName,SiteName,ServerIP,Method,UriStem,UriQuery,HttpStatus,Win32Status,BytesSent,BytesRecv,TimeTaken,ServerPort,UserAgent,Referer"
Enabling Compression and Performance
Enable static and dynamic content compression to reduce bandwidth consumption and improve response times:
# Enable static compression
Set-WebConfigurationProperty `
-Filter "system.webServer/httpCompression" `
-PSPath "IIS:" `
-Name "staticCompressionEnabled" -Value $true
# Enable dynamic compression
Set-WebConfigurationProperty `
-Filter "system.webServer/httpCompression" `
-PSPath "IIS:" `
-Name "dynamicCompressionEnabled" -Value $true
# Set compression directory
Set-WebConfigurationProperty `
-Filter "system.webServer/httpCompression" `
-PSPath "IIS:" `
-Name "directory" -Value "%SystemDrive%inetpubtempIIS Temporary Compressed Files"
# Enable output caching for static content
Add-WebConfigurationProperty `
-Filter "system.webServer/caching" `
-PSPath "IIS:Sitesmywebsite" `
-Name "profiles" `
-Value @{extension=".html"; policy="CacheUntilChange"; kernelCachePolicy="CacheUntilChange"}
IIS 10 on Windows Server 2019 is a production-grade web server suitable for hosting enterprise applications. Combine it with URL Rewrite for URL normalization and redirects, Application Request Routing (ARR) for reverse proxying and load balancing, and Web Application Firewall rules to protect against OWASP Top 10 vulnerabilities.