Introduction to IIS HTTP/2 on Windows Server 2019
HTTP/2 is the second major version of the HTTP protocol, standardised in RFC 7540. It introduces multiplexing (multiple concurrent requests over a single TCP connection), header compression (HPACK), server push, and binary framing — all of which significantly reduce latency and page load times compared to HTTP/1.1, especially over high-latency connections. IIS 10 on Windows Server 2019 supports HTTP/2 natively. However, HTTP/2 requires HTTPS — all major browsers only use HTTP/2 over TLS. This guide covers enabling, verifying, and optimising HTTP/2 on IIS Windows Server 2019, along with understanding its limitations and configuration options.
HTTP/2 Prerequisites on Windows Server 2019
HTTP/2 support in IIS requires:
1. Windows Server 2019 (or Windows Server 2016 with IIS 10).
2. TLS 1.2 or higher must be enabled (HTTP/2 browsers require TLS 1.2+).
3. A valid HTTPS binding on the site.
4. The client must support HTTP/2 (all modern browsers including Chrome, Firefox, Edge, and Safari do).
5. The application pool must be in 64-bit mode (HTTP/2 is not available to 32-bit application pools).
# Verify IIS version
Get-ItemProperty -Path "HKLM:SOFTWAREMicrosoftInetStp" | Select-Object MajorVersion, MinorVersion
# Verify HTTPS binding exists
Get-WebBinding | Where-Object Protocol -eq "https" |
Select-Object bindingInformation, sslFlags | Format-Table
# Verify TLS 1.2 is enabled (required for HTTP/2 clients)
$tls12 = Get-ItemProperty `
"HKLM:SYSTEMCurrentControlSetControlSecurityProvidersSCHANNELProtocolsTLS 1.2Server" `
-ErrorAction SilentlyContinue
Write-Host "TLS 1.2 Enabled: $($tls12.Enabled)"
HTTP/2 is Enabled by Default
Good news: HTTP/2 is enabled by default in IIS 10 on Windows Server 2019. There is no feature to install or enable — if your site has a valid HTTPS binding and the client supports HTTP/2, IIS will automatically negotiate HTTP/2. You can verify this is functioning and optionally disable or tune it.
# Verify HTTP/2 is enabled at the protocol level
# IIS uses the HTTP.sys kernel driver which handles HTTP/2 negotiation
# Check HTTP.sys settings
netsh http show UrlAcl
netsh http show sslcert
# Check HTTP/2 settings in registry (DoNotForbidHTTP2 should be 0 or absent to allow HTTP/2)
$http2Key = "HKLM:SYSTEMCurrentControlSetServicesHTTPParameters"
Get-ItemProperty -Path $http2Key -Name "EnableHttp2Tls" -ErrorAction SilentlyContinue
Get-ItemProperty -Path $http2Key -Name "EnableHttp2Cleartext" -ErrorAction SilentlyContinue
Verify HTTP/2 is Being Used
Test whether HTTP/2 is being negotiated by checking IIS access logs or using PowerShell:
# IIS logs include the protocol version in the cs-version field
# Enable cs-version logging field first
Set-WebConfigurationProperty `
-PSPath "IIS:SitesDefault Web Site" `
-Filter "system.applicationHost/sites/site/logFile" `
-Name "logExtFileFlags" `
-Value "Date,Time,ClientIP,UserName,ServerIP,Method,UriStem,UriQuery,HttpStatus,BytesRecv,TimeTaken,ProtocolVersion"
# After enabling, search logs for HTTP/2 entries
Get-Content "C:inetpublogsLogFilesW3SVC1u_ex$(Get-Date -Format 'yyMMdd').log" |
Where-Object { $_ -match "HTTP/2" } | Select-Object -First 10
# Test HTTP/2 negotiation using PowerShell (requires .NET 5+ or curl)
# Using curl (available in Windows 10/Server 2019)
# curl --http2 -I https://www.yoursite.com
# Using .NET WebRequest to check negotiated version
Add-Type -AssemblyName System.Net.Http
$handler = [System.Net.Http.HttpClientHandler]::new()
$handler.ServerCertificateCustomValidationCallback = [System.Net.Http.HttpClientHandler]::DangerousAcceptAnyServerCertificateValidator
$client = [System.Net.Http.HttpClient]::new($handler)
$response = $client.GetAsync("https://localhost").Result
Write-Host "HTTP Version: $($response.Version)"
Write-Host "Status: $($response.StatusCode)"
$client.Dispose()
Disable HTTP/2 on Specific Sites
While HTTP/2 is beneficial in most scenarios, some applications have compatibility issues with multiplexing (particularly older WebSocket implementations or applications that rely on HTTP/1.1 connection-per-request behaviour). You can disable HTTP/2 at the site level or globally:
# Disable HTTP/2 globally via registry (requires reboot)
$httpParams = "HKLM:SYSTEMCurrentControlSetServicesHTTPParameters"
Set-ItemProperty -Path $httpParams -Name "EnableHttp2Tls" -Value 0 -Type DWord
# Re-enable
Set-ItemProperty -Path $httpParams -Name "EnableHttp2Tls" -Value 1 -Type DWord
# Disable HTTP/2 for a specific IIS binding via SSL flags
# The sslFlags on the binding control whether HTTP/2 is allowed
# sslFlags: 0=HTTP/1.1, no SNI; 1=SNI; 2=CentralCertStore; 4=HTTP/2 supported (auto)
# Setting sslFlags to not include 4 disables HTTP/2 on that binding
# Alternatively, use the web.config to downgrade the protocol for specific applications
# Add the following to web.config to force HTTP/1.1 responses:
Set-WebConfigurationProperty `
-PSPath "IIS:SitesLegacyApp" `
-Filter "system.webServer/httpProtocol/customHeaders" `
-Name "." `
-Value @{name="Alt-Svc"; value="clear"} # Tell clients to not upgrade to HTTP/2
Configure Server Push
HTTP/2 Server Push allows the server to proactively send resources (CSS, JavaScript, fonts) to the client before the client requests them, eliminating round-trip delays. Configure server push via Link response headers with the rel=preload hint:
# Add Link headers for server push via IIS custom headers
# This tells IIS to push /css/style.css and /js/app.js with the initial HTML response
Set-WebConfigurationProperty `
-PSPath "IIS:SitesDefault Web Site" `
-Filter "system.webServer/httpProtocol/customHeaders" `
-Name "." `
-Value @{name="Link"; value="; rel=preload; as=style, ; rel=preload; as=script"}
# Or configure at the application level in web.config:
#
#
#
#
#
Optimise for HTTP/2 — Connection Limits and Streams
HTTP/2 multiplexes multiple streams over a single connection. Configure HTTP.sys stream limits to handle high-concurrency scenarios:
# HTTP/2 settings in HTTP.sys
# Max concurrent streams per connection (default 100)
Set-ItemProperty `
-Path "HKLM:SYSTEMCurrentControlSetServicesHTTPParameters" `
-Name "Http2MaxConcurrentStreams" -Value 100 -Type DWord
# Initial window size for HTTP/2 flow control (default 65535)
Set-ItemProperty `
-Path "HKLM:SYSTEMCurrentControlSetServicesHTTPParameters" `
-Name "Http2InitialWindowSize" -Value 65535 -Type DWord
# These changes require a restart of the HTTP service
Restart-Service -Name W3SVC, HTTP
Monitor HTTP/2 Performance
# Check IIS performance counters for HTTP/2 activity
Get-Counter -ListSet "Web Service" |
Select-Object -ExpandProperty Counter |
Where-Object { $_ -match "HTTP/2|Current" }
# Monitor active connections
Get-Counter "Web Service(_Total)Current Connections" -SampleInterval 5 -MaxSamples 5
# Check HTTP.sys counters
Get-Counter -ListSet "HTTP Service" | Select-Object -ExpandProperty Counter
HTTP/2 and Load Balancing Considerations
HTTP/2 multiplexing means multiple requests share one TCP connection. Traditional load balancers that operate at the TCP connection level (Layer 4) will route all multiplexed requests to the same backend server, defeating load balancing. For proper HTTP/2 load balancing, use a Layer 7 load balancer (such as ARR configured with proper HTTP/2 handling) or terminate HTTP/2 at the load balancer and use HTTP/1.1 to backends.
# When using ARR as HTTP/2 load balancer:
# ARR 3.0 supports HTTP/2 client-side but may use HTTP/1.1 to backends
# This is acceptable and common — HTTP/2 benefits apply on the client edge
# Check ARR HTTP version to backend
Get-WebConfigurationProperty `
-PSPath "IIS:" `
-Filter "system.webServer/proxy" `
-Name "httpVersion"
# Set ARR to use HTTP/1.1 to backends (recommended for compatibility)
Set-WebConfigurationProperty `
-PSPath "IIS:" `
-Filter "system.webServer/proxy" `
-Name "httpVersion" -Value "Http11"
Summary
HTTP/2 on IIS Windows Server 2019 is enabled by default for any HTTPS site — no installation or configuration is required to start benefiting from multiplexing and header compression. Validate that HTTP/2 is being negotiated by checking IIS access logs for HTTP/2 protocol version entries. Use Server Push headers to proactively deliver critical resources. If legacy applications have compatibility issues with HTTP/2 multiplexing, disable it selectively per site via registry settings. For load-balanced deployments, terminate HTTP/2 at the edge and use HTTP/1.1 to backend servers to maintain proper distribution of requests.