Introduction to IIS WebSockets on Windows Server 2019

WebSockets provide a full-duplex, persistent communication channel over a single TCP connection between a client and server. Unlike standard HTTP request/response, WebSockets allow the server to push data to clients without the client polling. This is used in real-time applications including chat systems, collaborative tools, live dashboards, financial tickers, multiplayer games, and IoT device management interfaces. IIS 10 on Windows Server 2019 includes native WebSocket support through the WebSocket Protocol feature, which requires no additional modules. This guide covers installation, ASP.NET and Node.js integration, configuration, and troubleshooting.

Install WebSocket Protocol Feature

# Install the WebSocket Protocol IIS feature
Install-WindowsFeature -Name Web-WebSockets -IncludeManagementTools

# Verify installation
Get-WindowsFeature -Name Web-WebSockets

# Confirm the WebSocket module is loaded in IIS
Get-WebConfiguration -Filter "system.webServer/globalModules/add" |
    Where-Object name -match "WebSocket" | Select-Object name, image

Understanding IIS WebSocket Architecture

IIS handles the HTTP Upgrade handshake that converts a standard HTTP/1.1 connection to a WebSocket connection. When a client sends an HTTP GET request with the headers Connection: Upgrade and Upgrade: websocket, IIS completes the handshake with a 101 Switching Protocols response and hands the connection off to the application. The application then owns the bidirectional socket and can send and receive frames at any time until the connection is closed.

For .NET applications, IIS passes the WebSocket connection to the application via the System.Net.WebSockets.AspNetWebSocket API. Application pool pipeline must be in Integrated mode for WebSockets to work.

Verify Application Pool Configuration

# WebSockets require Integrated pipeline mode — verify
Get-WebConfiguration -Filter "system.applicationHost/applicationPools/add" |
    Where-Object { $_.name -eq "MyWebSocketApp_Pool" } |
    Select-Object name, managedPipelineMode

# Ensure Integrated mode (0 = Integrated, 1 = Classic)
Set-ItemProperty -Path "IIS:AppPoolsMyWebSocketApp_Pool" `
    -Name "managedPipelineMode" -Value 0

# WebSocket connections are long-lived — disable idle timeout
Set-ItemProperty -Path "IIS:AppPoolsMyWebSocketApp_Pool" `
    -Name "processModel.idleTimeout" -Value "00:00:00"

# Set startup mode to AlwaysRunning
Set-ItemProperty -Path "IIS:AppPoolsMyWebSocketApp_Pool" `
    -Name "startMode" -Value 1

Configure WebSocket Connection Limits

Each WebSocket connection holds a TCP connection open for its lifetime. On a busy server, you may hit default IIS connection limits. Tune these to support the expected number of concurrent WebSocket connections:

# View current connection limits
Get-WebConfigurationProperty `
    -PSPath "IIS:" `
    -Filter "system.applicationHost/sites/site[@name='MyWebSocketSite']/limits" `
    -Name "*"

# Set maximum concurrent connections (0 = unlimited)
Set-WebConfigurationProperty `
    -PSPath "IIS:SitesMyWebSocketSite" `
    -Filter "limits" `
    -Name "maxConnections" -Value 0

# Set connection timeout (WebSocket connections should not time out like HTTP)
Set-WebConfigurationProperty `
    -PSPath "IIS:SitesMyWebSocketSite" `
    -Filter "limits" `
    -Name "connectionTimeout" -Value "01:00:00"   # 1 hour

ASP.NET WebSocket Example — Echo Server

A simple ASP.NET WebSocket echo handler demonstrates the server-side implementation pattern. This requires an ASP.NET web application in Integrated mode:

// WebSocket handler in ASP.NET (WebSocketHandler.ashx or in Global.asax)
// Place this in a .ashx HTTP Handler file:

// WebSocketEcho.ashx:
// 
// using System;
// using System.Net.WebSockets;
// using System.Threading;
// using System.Web;
// using System.Web.WebSockets;
//
// public class WebSocketEcho : IHttpHandler {
//     public void ProcessRequest(HttpContext context) {
//         if (context.IsWebSocketRequest) {
//             context.AcceptWebSocketRequest(HandleWebSocket);
//         }
//     }
//
//     private async System.Threading.Tasks.Task HandleWebSocket(AspNetWebSocketContext ctx) {
//         WebSocket ws = ctx.WebSocket;
//         byte[] buffer = new byte[4096];
//         while (ws.State == WebSocketState.Open) {
//             var result = await ws.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None);
//             if (result.MessageType == WebSocketMessageType.Close) {
//                 await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
//             } else {
//                 await ws.SendAsync(new ArraySegment(buffer, 0, result.Count),
//                     WebSocketMessageType.Text, result.EndOfMessage, CancellationToken.None);
//             }
//         }
//     }
//     public bool IsReusable { get { return false; } }
// }

Configure WebSockets with Node.js Behind IIS

IIS can proxy WebSocket connections to a Node.js application using the iisnode module or ARR reverse proxy. This is common for deploying Socket.io applications behind IIS:

# Install iisnode module for Node.js hosting
# Download from: https://github.com/Azure/iisnode/releases
# msiexec /i iisnode-full-v0.2.26-x64.msi /quiet

# web.config for iisnode + Socket.io:
$iisnodeConfig = @'


  
    
    
      
    
    
      
        
          
        
        
          
        
        
          
            
          
          
        
      
    
  

'@

Configure WebSocket Proxy with ARR

When using ARR to proxy WebSocket connections to a backend server, ARR automatically upgrades the connection if the backend supports WebSockets:

# Enable WebSocket proxying in ARR
Set-WebConfigurationProperty `
    -PSPath "IIS:" `
    -Filter "system.webServer/proxy" `
    -Name "httpVersion" -Value "Http11"   # Ensure HTTP/1.1 for WebSocket upgrade

# The reverse proxy rewrite rule works for WebSockets automatically
# ARR detects the Upgrade: websocket header and tunnels the connection
# No additional WebSocket-specific configuration needed in ARR

# Verify ARR is handling WebSocket upgrades by checking access logs
Get-Content "C:inetpublogsLogFilesW3SVC1u_ex*.log" | 
    Select-String "101" | Select-Object -Last 20

Firewall and Timeout Configuration

# Ensure HTTPS (443) traffic is allowed for secure WebSockets (wss://)
Get-NetFirewallRule | Where-Object { $_.LocalPort -eq 443 -and $_.Direction -eq "Inbound" }

# Disable idle connection timeout at the HTTP.sys level for WebSocket sites
# WebSocket connections must not be killed by HTTP.sys idle timeouts
netsh http show timeout
# Set connection timeout to 0 to disable (WebSockets manage their own heartbeats)
# netsh http set timeout idleconnectiontimeout=0  # Use cautiously

Troubleshoot WebSocket Connections

# Check IIS logs for 101 Switching Protocols responses
Get-Content "C:inetpublogsLogFilesW3SVC1u_ex$(Get-Date -Format 'yyMMdd').log" |
    Where-Object { $_ -match " 101 " } | Select-Object -Last 20

# Check for 426 Upgrade Required errors (WebSocket not supported)
Get-Content "C:inetpublogsLogFilesW3SVC1u_ex$(Get-Date -Format 'yyMMdd').log" |
    Where-Object { $_ -match " 426 " }

# Verify Web-WebSockets feature is installed
Get-WindowsFeature -Name Web-WebSockets | Select-Object Name, InstallState

# Test WebSocket connectivity using PowerShell ClientWebSocket
$ws = [System.Net.WebSockets.ClientWebSocket]::new()
$uri = [System.Uri]::new("ws://localhost/echo")
$cts = [System.Threading.CancellationTokenSource]::new(5000)
try {
    $ws.ConnectAsync($uri, $cts.Token).Wait()
    Write-Host "WebSocket connected. State: $($ws.State)"
} catch {
    Write-Host "Connection failed: $_"
} finally {
    $ws.Dispose()
}

Summary

WebSocket support in IIS on Windows Server 2019 is built into the platform via the Web-WebSockets feature and requires no third-party modules for ASP.NET applications. Key configuration points are: use Integrated pipeline mode, disable idle connection timeouts on long-lived connections, tune max connections for high-concurrency scenarios, and configure ARR correctly when proxying WebSocket traffic to backend servers. WebSockets deliver real-time bidirectional communication suitable for modern web applications without the overhead of polling or long-polling techniques.