Skip to content

IIS sites, applications and virtual directories: the four-layer model

Verified 2 min read

Understand site vs application vs virtual directory vs application pool, create a lab site on 8080, and choose the isolation boundary deliberately.

Tested onStandard / Datacenter · Build 26100.33650 · x64
E1
Build
26100.33650
Edition
Standard / Datacenter
Architecture
x64
Support
Documentation (E1)
Evidence
E1Official documentation
Verified
2026-10-29
Test machine
IIS Manager UI reference: VM 361 Windows 11 26H2; Site Lab on :8080; Server 2025 cmdlets E1
Page type
Concept
IIS Manager Sites view showing Default Web Site on port 80 and a Lab site on port 8080.
IIS Manager Sites on VM 361: Default Web Site (:80) and Lab (:8080). Windows 11 UI reference; Server 2025 role steps are E1. Click to open full size.

Four different things:

Object What it is Own binding? Own app pool?
Site A web endpoint: binding + root + app pool Yes Yes
Application A path segment inside a site (e.g. /shop) No (inherits site binding) Yes
Virtual directory A folder mapped to a URL path No No
Application pool The worker-process boundary No —

Use a site for a different hostname/port. Use an application for a separate app under the same host. Use a virtual directory only for static content that does not need its own process.

IIS Manager on VM 361 shows:

  • Default Web Site, id 1, http/*:80:
  • Lab, id 2, http/*:8080:, root C:\inetpub\lab, app pool labpool
IIS Manager Lab site selected, showing the binding on port 8080 and the actions pane.
The Lab site selected: binding 8080, path C:\inetpub\lab, app pool assigned. Captured on Windows 11 26H2.

Create it with PowerShell (WebAdministration module):

Terminal window
Import-Module WebAdministration
New-Item -ItemType Directory -Force C:\inetpub\lab | Out-Null
New-WebAppPool -Name 'labpool'
New-WebSite -Name 'Lab' -PhysicalPath 'C:\inetpub\lab' -Port 8080 -ApplicationPool 'labpool'

Or with appcmd:

Terminal window
appcmd add apppool /name:labpool /managedRuntimeVersion:""
appcmd add site /name:Lab /physicalPath:C:\inetpub\lab /bindings:"http/*:8080:"
appcmd set app "Lab/" /applicationPool:labpool

managedRuntimeVersion:"" means No Managed Code — correct for a static site or a reverse-proxied Node/Python app. Do not leave it on v4.0 unless the app needs .NET Framework.

Terminal window
New-WebApplication -Site 'Lab' -Name 'shop' -PhysicalPath 'C:\apps\shop' -ApplicationPool 'shoppool'

This creates http://host:8080/shop with its own app pool. The parent site’s web.config still applies unless the child overrides it. That inheritance is useful, and a common source of “it works at the root but not under /shop” bugs.

Terminal window
New-WebVirtualDirectory -Site 'Lab' -Name 'docs' -PhysicalPath 'D:\StaticDocs'

/docs maps to a folder but uses the parent application’s app pool. It is not an isolation boundary.

Terminal window
Get-Website | Select-Object Name, State, PhysicalPath, ApplicationPool
Get-WebApplication -Site 'Lab'
Get-WebVirtualDirectory -Site 'Lab'
Invoke-WebRequest http://localhost:8080/ -UseBasicParsing | Select-Object StatusCode

Then check from another machine on the allowed network; a localhost-only test does not prove the firewall/binding is correct.

  • Binding conflict: two sites on the same IP:port with no host header/SNI. IIS starts one and fails the other.
  • Wrong app pool: static site on DefaultAppPool with .NET loaded; unnecessary surface.
  • Virtual directory permissions: the app pool identity needs NTFS read on the target folder, not just IIS “connect as”.
  • Nested config: a parent web.config can block a child app; read the 500.19 error instead of deleting config.
  • Default site left running: it may answer requests for an unbound hostname.

The UI is Windows 11 26H2. The cmdlets and object model are the same family as Server 2025, but Server-specific role installation remains E1 until a Server 2025 VM reproduces it.

Sources