Overview
This guide covers the recommended folder layout, IIS configuration steps, and an optional PowerShell script to automate the setup of a new IIS website on Windows Server 2025.
Best Practice Folder Layout
- Create one folder per site outside of wwwroot
- Example:
C:\inetpub\MyNewSite
Step 1 - Create the Site Folder
- Create the folder for your site (example:
C:\inetpub\MyNewSite) - Place your site files inside this folder
Step 2 - Create a Dedicated Application Pool
- Open IIS Manager
- Navigate to Application Pools and click Add Application Pool
- Configure the following:
- Name: MyNewSiteAppPool
- .NET CLR: use the required runtime, or select No Managed Code for static sites
- Pipeline: Integrated
Step 3 - Set NTFS Permissions on the Site Folder
- Grant Modify permissions to the App Pool identity only:
IIS AppPool\MyNewSiteAppPool - Remove unnecessary write permissions for Users or Everyone
Step 4 - Add the Website
- In IIS Manager, navigate to Sites and click Add Website
- Configure the following:
- Site name: MyNewSite
- Application pool: MyNewSiteAppPool
- Physical path: C:\inetpub\MyNewSite
- Binding: HTTP on port 80 with a host name (example: mynewsite.local)
Step 5 - Add HTTPS (Optional)
- Add an HTTPS binding and select a certificate when available
- See the separate certificate guide for full details
Step 6 - Test Locally
- Add a hosts file entry for testing:
127.0.0.1 mynewsite.local - Browse to
http://mynewsite.localorhttps://mynewsite.localto confirm the site loads
PowerShell Script - Create Folder, App Pool, Site, and Permissions
Run the following in an elevated PowerShell session to automate the full setup:
# Variables
$SiteName = "MyNewSite"
$AppPool = "MyNewSiteAppPool"
$PhysicalPath = "C:\inetpub\MyNewSite"
$HttpHost = "mynewsite.local"
$HttpPort = 80
Import-Module WebAdministration
# 1) Create folder if it does not exist
if (-not (Test-Path -LiteralPath $PhysicalPath)) {
New-Item -ItemType Directory -Path $PhysicalPath | Out-Null
}
# 2) Create a dedicated Application Pool
if (-not (Test-Path IIS:\AppPools\$AppPool)) {
New-WebAppPool -Name $AppPool | Out-Null
Set-ItemProperty "IIS:\AppPools\$AppPool" -Name "managedPipelineMode" -Value "Integrated"
# Use No Managed Code for static sites:
# Set-ItemProperty "IIS:\AppPools\$AppPool" -Name "managedRuntimeVersion" -Value ""
}
# 3) Grant NTFS permissions to the App Pool identity
$aclCmd = 'icacls "{0}" /grant "IIS AppPool\{1}:(OI)(CI)(M)" /T' -f $PhysicalPath, $AppPool
cmd.exe /c $aclCmd
# 4) Create the website and bind HTTP
if (-not (Get-Website -Name $SiteName -ErrorAction SilentlyContinue)) {
New-Website -Name $SiteName -PhysicalPath $PhysicalPath -Port $HttpPort -HostHeader $HttpHost -ApplicationPool $AppPool | Out-Null
} else {
if (-not (Get-WebBinding -Name $SiteName -Protocol "http" -ErrorAction SilentlyContinue | Where-Object { $_.bindingInformation -match ":$HttpPort:$HttpHost" })) {
New-WebBinding -Name $SiteName -Protocol "http" -Port $HttpPort -HostHeader $HttpHost | Out-Null
}
}
Write-Host "Site '$SiteName' created and bound to http://$HttpHost:$HttpPort"
Optional - Create a Self-Signed Certificate and Add HTTPS Binding
For quick internal testing, use the following script to generate a self-signed certificate and bind it to HTTPS:
$HttpsPort = 443
$DnsName = $HttpHost
$cert = New-SelfSignedCertificate -DnsName $DnsName -CertStoreLocation "cert:\LocalMachine\My" -FriendlyName "$SiteName SelfSigned" -KeyLength 2048 -HashAlgorithm sha256 -KeyExportPolicy Exportable -NotAfter (Get-Date).AddYears(1)
if (-not (Get-WebBinding -Name $SiteName -Protocol "https" -ErrorAction SilentlyContinue | Where-Object { $_.bindingInformation -match ":$HttpsPort:$DnsName" })) {
New-WebBinding -Name $SiteName -Protocol "https" -Port $HttpsPort -HostHeader $DnsName | Out-Null
}
$bindingPath = "IIS:\SslBindings.0.0.0!$HttpsPort!$DnsName"
if (-not (Test-Path $bindingPath)) {
New-Item $bindingPath -Thumbprint $cert.Thumbprint -SSLFlags 1 | Out-Null
} else {
Set-Item -Path $bindingPath -Thumbprint $cert.Thumbprint -SSLFlags 1
}
Write-Host "HTTPS binding added for https://$DnsName:$HttpsPort using a self-signed certificate."
Notes
- Always run PowerShell as Administrator when executing these scripts
- For production environments, use a trusted CA certificate - self-signed certificates will trigger browser warnings
- Keep one site per Application Pool for better process isolation and security