How to Host Multiple Websites with IIS Bindings on Windows Server 2025

One of IIS’s most powerful capabilities is its ability to serve multiple distinct websites from a single Windows Server 2025 instance using a technique called virtual hosting. By leveraging host headers — the Host: value that every modern browser sends with each HTTP request — IIS can route incoming traffic to the correct site even when all sites share the same IP address and port. This approach, known as name-based or host header virtual hosting, allows you to consolidate dozens of websites onto a single server while keeping each site’s content, configuration, and application pool entirely isolated from the others. This guide covers the full process: creating sites with host header bindings, adding HTTPS bindings with SNI, configuring dedicated application pools, and managing folder permissions.

Prerequisites

  • Windows Server 2025 with IIS 10.0 already installed (see the IIS installation guide if needed)
  • The WebAdministration PowerShell module available (Import-Module WebAdministration)
  • Administrator-level access on the server
  • DNS entries (or hosts file entries for testing) resolving each hostname to the server’s IP
  • TLS certificates for any HTTPS-enabled sites (self-signed acceptable for testing)

Step 1: Plan the Site Layout

Before running any commands, establish a consistent folder structure under %SystemDrive%inetpub. A common convention is to create one subfolder per domain or application under C:inetpubsites:

# Create a top-level container and individual site roots
New-Item -ItemType Directory -Path "C:inetpubsitesalpha.example.com"
New-Item -ItemType Directory -Path "C:inetpubsitesbeta.example.com"
New-Item -ItemType Directory -Path "C:inetpubsitesgamma.example.com"

# Place placeholder index files for each
Set-Content "C:inetpubsitesalpha.example.comindex.html" "

Alpha Site

" Set-Content "C:inetpubsitesbeta.example.comindex.html" "

Beta Site

" Set-Content "C:inetpubsitesgamma.example.comindex.html" "

Gamma Site

"

Keeping each site in a named subfolder makes it easy to locate content, apply per-directory policies, and back up individual sites without affecting others.

Step 2: Create Dedicated Application Pools

Running each site in its own application pool provides process isolation: a crash or runaway request in one pool does not affect the others, and you can recycle individual pools without disrupting the rest of the server.

Import-Module WebAdministration

$sites = @("alpha.example.com", "beta.example.com", "gamma.example.com")

foreach ($site in $sites) {
    $poolName = "$site-Pool"
    New-WebAppPool -Name $poolName
    Set-ItemProperty "IIS:AppPools$poolName" -Name managedRuntimeVersion -Value 'v4.0'
    Set-ItemProperty "IIS:AppPools$poolName" -Name managedPipelineMode   -Value 'Integrated'
    Set-ItemProperty "IIS:AppPools$poolName" -Name processModel.idleTimeout -Value '00:20:00'
    Write-Host "Created pool: $poolName"
}

Step 3: Create Sites with Host Header Bindings

The -HostHeader parameter of New-WebSite is what enables name-based virtual hosting. IIS reads the Host: header from incoming HTTP requests and directs them to the matching site.

# Create each site with its corresponding host header binding
New-WebSite -Name "alpha.example.com" `
            -PhysicalPath "C:inetpubsitesalpha.example.com" `
            -Port 80 `
            -IPAddress "*" `
            -HostHeader "alpha.example.com" `
            -ApplicationPool "alpha.example.com-Pool" `
            -Force

New-WebSite -Name "beta.example.com" `
            -PhysicalPath "C:inetpubsitesbeta.example.com" `
            -Port 80 `
            -IPAddress "*" `
            -HostHeader "beta.example.com" `
            -ApplicationPool "beta.example.com-Pool" `
            -Force

New-WebSite -Name "gamma.example.com" `
            -PhysicalPath "C:inetpubsitesgamma.example.com" `
            -Port 80 `
            -IPAddress "*" `
            -HostHeader "gamma.example.com" `
            -ApplicationPool "gamma.example.com-Pool" `
            -Force

# Start all new sites
$sites | ForEach-Object { Start-WebSite -Name $_ }

# List all sites and their state
Get-Website | Select-Object Name, State, PhysicalPath

Step 4: Add Additional Bindings to an Existing Site

A single IIS site can answer to multiple host names — for example, both the www-prefixed and bare-domain versions of a hostname. Use New-WebBinding to add extra bindings without touching the original site definition:

# Add www alias binding to alpha site
New-WebBinding -Name "alpha.example.com" `
               -Protocol "http" `
               -Port 80 `
               -IPAddress "*" `
               -HostHeader "www.alpha.example.com"

# Verify all bindings on this site
Get-WebBinding -Name "alpha.example.com"

The binding string in IIS is expressed in the format IP:Port:HostHeader. An asterisk (*) in the IP field means the binding applies to all interfaces on the server.

Step 5: Configure SSL Bindings with SNI

Server Name Indication (SNI) allows multiple HTTPS sites to share a single IP address, each presenting its own certificate. Without SNI, each HTTPS site needs a dedicated IP. Windows Server 2025 fully supports SNI with IIS 10.0.

First, ensure you have certificates in the Local Machine certificate store. For testing, generate self-signed certs:

# Create self-signed certificates for each site (valid 365 days)
$alphaCert = New-SelfSignedCertificate -DnsName "alpha.example.com" `
    -CertStoreLocation "Cert:LocalMachineMy" -NotAfter (Get-Date).AddDays(365)

$betaCert  = New-SelfSignedCertificate -DnsName "beta.example.com" `
    -CertStoreLocation "Cert:LocalMachineMy" -NotAfter (Get-Date).AddDays(365)

$gammaCert = New-SelfSignedCertificate -DnsName "gamma.example.com" `
    -CertStoreLocation "Cert:LocalMachineMy" -NotAfter (Get-Date).AddDays(365)

# Confirm thumbprints
Write-Host "Alpha: $($alphaCert.Thumbprint)"
Write-Host "Beta:  $($betaCert.Thumbprint)"
Write-Host "Gamma: $($gammaCert.Thumbprint)"

Now add HTTPS bindings with SNI enabled (-SslFlags 1 enables SNI):

# Add HTTPS binding for alpha site with SNI
New-WebBinding -Name "alpha.example.com" `
               -Protocol "https" `
               -Port 443 `
               -IPAddress "*" `
               -HostHeader "alpha.example.com" `
               -SslFlags 1

# Bind the certificate to the new HTTPS binding
$alphaBinding = Get-WebBinding -Name "alpha.example.com" -Protocol "https"
$alphaBinding.AddSslCertificate($alphaCert.Thumbprint, "My")

# Repeat for beta and gamma
New-WebBinding -Name "beta.example.com" `
               -Protocol "https" -Port 443 `
               -IPAddress "*" -HostHeader "beta.example.com" -SslFlags 1
$betaBinding = Get-WebBinding -Name "beta.example.com" -Protocol "https"
$betaBinding.AddSslCertificate($betaCert.Thumbprint, "My")

New-WebBinding -Name "gamma.example.com" `
               -Protocol "https" -Port 443 `
               -IPAddress "*" -HostHeader "gamma.example.com" -SslFlags 1
$gammaBinding = Get-WebBinding -Name "gamma.example.com" -Protocol "https"
$gammaBinding.AddSslCertificate($gammaCert.Thumbprint, "My")

Step 6: Set Folder Permissions for IIS_IUSRS

Each site’s physical path must be readable by the application pool worker process identity. The built-in IIS_IUSRS group covers all application pool identities. For tighter control, grant access to the specific virtual account (IIS AppPool<PoolName>):

$siteRoots = @{
    "alpha.example.com" = "C:inetpubsitesalpha.example.com"
    "beta.example.com"  = "C:inetpubsitesbeta.example.com"
    "gamma.example.com" = "C:inetpubsitesgamma.example.com"
}

foreach ($entry in $siteRoots.GetEnumerator()) {
    $poolAccount = "IIS AppPool$($entry.Key)-Pool"
    icacls $entry.Value /grant "${poolAccount}:(OI)(CI)R" /T
    Write-Host "Permissions set for $($entry.Key)"
}

Step 7: Test with the Hosts File

Before DNS is configured, you can test host-header routing by adding entries to the hosts file on a client machine (C:WindowsSystem32driversetchosts on Windows, /etc/hosts on Linux/macOS):

192.168.1.100   alpha.example.com www.alpha.example.com
192.168.1.100   beta.example.com
192.168.1.100   gamma.example.com

Then open a browser and navigate to each hostname. IIS reads the Host: header and serves content from the correct site root. Confirm from the server with:

Invoke-WebRequest -Uri "http://alpha.example.com" -UseBasicParsing | Select-Object StatusCode
Invoke-WebRequest -Uri "http://beta.example.com"  -UseBasicParsing | Select-Object StatusCode
Invoke-WebRequest -Uri "http://gamma.example.com" -UseBasicParsing | Select-Object StatusCode

Conclusion

You have configured name-based virtual hosting in IIS on Windows Server 2025, hosting three distinct websites on a single server using host headers. Each site has its own dedicated application pool for process isolation, its own folder with correct permissions, and both HTTP and HTTPS bindings leveraging SNI to share a single IP address across multiple TLS certificates. This pattern scales efficiently — you can add new sites by repeating the same steps without requiring additional IP addresses or network changes. As a next step, consider automating site provisioning with a PowerShell script that accepts domain name and content path as parameters, making it trivial to onboard new tenants in a multi-site hosting environment.