How to Configure Windows Server 2016 IIS Web Server
Internet Information Services (IIS) 10.0 is the web server platform included with Windows Server 2016. It supports HTTP, HTTPS, FTP, and WebSockets, and is used to host websites, web applications, REST APIs, and web services. This guide covers installing IIS, creating websites, configuring bindings and SSL certificates, setting up application pools, enabling logging, and hardening the server.
Step 1: Install IIS Web Server Role
# Install IIS with common features and management tools
Install-WindowsFeature -Name Web-Server -IncludeManagementTools
# Install additional IIS features
Install-WindowsFeature -Name Web-Server, Web-Common-Http, Web-Static-Content, Web-Default-Doc, `
Web-Http-Errors, Web-Http-Redirect, Web-Asp-Net45, Web-Net-Ext45, `
Web-ISAPI-Ext, Web-ISAPI-Filter, Web-Http-Logging, Web-Request-Monitor, `
Web-Windows-Auth, Web-Basic-Auth, Web-Stat-Compression, Web-Dyn-Compression, `
Web-Mgmt-Console, Web-Mgmt-Tools, Web-Scripting-Tools
# Verify IIS is installed and running
Get-Service W3SVC | Select-Object Status, StartType
Invoke-WebRequest -Uri "http://localhost" -UseBasicParsing | Select-Object StatusCode
Step 2: Understand the IIS Directory Structure
By default, IIS creates a default website with its content in C:inetpubwwwroot. The IIS configuration is stored in C:WindowsSystem32inetsrvconfigapplicationHost.config. Best practice is to store website content on a non-system drive (e.g., D:Websites).
# Create directories for website content
New-Item -ItemType Directory -Path "D:WebsitesMySitewwwroot" -Force
New-Item -ItemType Directory -Path "D:WebsitesMySitelogs" -Force
# Create a simple test HTML page
Set-Content -Path "D:WebsitesMySitewwwrootindex.html" -Value "My IIS Website
"
Step 3: Create a New Website
# Import the WebAdministration module
Import-Module WebAdministration
# Create a new application pool
New-WebAppPool -Name "MySitePool"
Set-ItemProperty "IIS:AppPoolsMySitePool" -Name processModel.identityType -Value ApplicationPoolIdentity
Set-ItemProperty "IIS:AppPoolsMySitePool" -Name managedRuntimeVersion -Value "v4.0"
# Create a new IIS website bound to port 80 on a specific IP
New-Website -Name "MySite" `
-PhysicalPath "D:WebsitesMySitewwwroot" `
-ApplicationPool "MySitePool" `
-Port 80 `
-IPAddress "*" `
-HostHeader "www.example.com" `
-Force
# Start the website
Start-Website -Name "MySite"
# Verify the website is running
Get-Website -Name "MySite" | Select-Object Name, State, PhysicalPath, ApplicationPool
Step 4: Configure HTTPS with an SSL Certificate
Create a self-signed certificate for testing, or import a certificate from a trusted Certificate Authority for production:
# Create a self-signed certificate for testing
$cert = New-SelfSignedCertificate `
-DnsName "www.example.com" `
-CertStoreLocation "cert:LocalMachineMy" `
-NotAfter (Get-Date).AddYears(2)
$certThumbprint = $cert.Thumbprint
Write-Host "Certificate Thumbprint: $certThumbprint"
# Add an HTTPS binding to the website
New-WebBinding -Name "MySite" -Protocol "https" -Port 443 -IPAddress "*" -HostHeader "www.example.com"
# Bind the SSL certificate to the HTTPS binding
$binding = Get-WebBinding -Name "MySite" -Protocol "https"
$binding.AddSslCertificate($certThumbprint, "My")
# Verify the binding
Get-WebBinding -Name "MySite"
Step 5: Configure HTTP to HTTPS Redirect
# Enable the HTTP Redirect feature if not installed
Install-WindowsFeature -Name Web-Http-Redirect
# Configure HTTP redirect to HTTPS for the site
Set-WebConfiguration system.webServer/httpRedirect "IIS:SitesMySite" -Value @{
enabled = $true
destination = "https://www.example.com"
exactDestination = $false
httpResponseStatus = "Permanent"
}
Step 6: Configure IIS Logging
# Set the log file directory for a website
Set-ItemProperty "IIS:SitesMySite" -Name logFile.directory -Value "D:WebsitesMySitelogs"
# Set log format to W3C
Set-ItemProperty "IIS:SitesMySite" -Name logFile.logFormat -Value "W3C"
# Enable logging of extra fields (useful for analytics)
Set-ItemProperty "IIS:SitesMySite" -Name logFile.logExtFileFlags `
-Value "Date,Time,ClientIP,UserName,ServerIP,Method,UriStem,UriQuery,HttpStatus,BytesSent,BytesRecv,TimeTaken"
# Verify logging configuration
Get-ItemProperty "IIS:SitesMySite" -Name logFile
Step 7: Configure Application Pool Recycling
# Set application pool to recycle daily at 3:00 AM instead of after 1740 minutes (default)
$appPool = "MySitePool"
# Disable regular time interval recycling
Set-ItemProperty "IIS:AppPools$appPool" -Name recycling.periodicRestart.time -Value "00:00:00"
# Add a specific recycle time (3:00 AM)
Clear-ItemProperty "IIS:AppPools$appPool" -Name recycling.periodicRestart.schedule
Add-WebConfigurationProperty -PSPath "IIS:AppPools$appPool" `
-Filter "system.applicationHost/applicationPools/add[@name='$appPool']/recycling/periodicRestart/schedule" `
-Name "." -Value @{value="03:00:00"}
# Set memory limit (recycle if app pool exceeds 500 MB)
Set-ItemProperty "IIS:AppPools$appPool" -Name recycling.periodicRestart.privateMemory -Value 512000
Step 8: Harden IIS Security
# Remove the "Server" response header to hide IIS version information
# Add urlScan or use the following rewrite rule approach
Import-Module WebAdministration
# Disable directory browsing
Set-WebConfigurationProperty -Filter /system.webServer/directoryBrowse `
-PSPath "IIS:SitesMySite" -Name enabled -Value False
# Disable HTTP TRACE method
Add-WebConfigurationProperty -Filter system.webServer/security/requestFiltering/verbs `
-PSPath "IIS:SitesMySite" `
-Name "." -Value @{verb="TRACE"; allowed="false"}
# Set maximum URL length to prevent buffer overflow attacks
Set-WebConfigurationProperty -Filter system.webServer/security/requestFiltering/requestLimits `
-PSPath "IIS:SitesMySite" -Name maxUrl -Value 2048
# Remove X-Powered-By header
Remove-WebConfigurationProperty -PSPath "IIS:SitesMySite" `
-Filter system.webServer/httpProtocol/customHeaders -Name "." `
-AtElement @{name="X-Powered-By"}
Step 9: Test and Verify the Website
# Test the website locally
Invoke-WebRequest -Uri "http://localhost" -UseBasicParsing | Select-Object StatusCode, StatusDescription
# Test with a specific host header
Invoke-WebRequest -Uri "http://www.example.com" -UseBasicParsing -Headers @{Host="www.example.com"} | `
Select-Object StatusCode
# Check IIS site status
Get-Website | Select-Object Name, State, Bindings
# Restart a specific site
Restart-WebItem -PSPath "IIS:SitesMySite"
# Restart the entire IIS service
iisreset /restart
IIS 10.0 is now configured on Windows Server 2016 with a production-ready website, HTTPS support, proper application pool configuration, logging, and security hardening. IIS can host multiple websites on the same server using host headers and different port bindings. Regularly review IIS logs, apply Windows Updates, and audit your configuration to maintain a secure web server environment.