HTTP/2 is enabled by default on Windows Server 2016 and later, but HTTP/3 is not, and both need specific registry keys to behave correctly behind a proxy or load balancer. This guide covers enabling HTTP/3 over QUIC on IIS, verifying HTTP/2 is actually negotiating, and the TLS cipher suites that must be present for either to work.
Prerequisites and version support
| Windows Server | HTTP/2 | HTTP/3 (QUIC) | Notes |
|---|---|---|---|
| 2016 | Yes, on by default | No | Needs TLS 1.2+ ciphers |
| 2019 | Yes | No | Registry tuning available |
| 2022 | Yes | Yes (build 20348+) | Enable via registry, IIS 10 |
| 2025 | Yes | Yes | Best QUIC support, HTTP.sys native |
HTTP/2 requires HTTPS/TLS in practice — browsers will not negotiate h2 over cleartext, with one exception for h2c that IIS does not use. So TLS must be correct first. If HTTPS is not yet working, start with the IIS HTTPS bindings and certificates guide.
Step 1: confirm HTTP/2 in the registry
HTTP/2 settings live under HTTP.sys, not in IIS. Check the current state and set them explicitly.
# Inspect HTTP/2 parameters (HTTP.sys)
$base = 'HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters'
Get-ItemProperty -Path $base |
Select-Object EnableHttp2Tls, EnableHttp2Cleartext, Http2MaxSettingsPerFrame, Http2MaxSettingsPerMinute
# Enable HTTP/2 over TLS (default 1 on modern builds)
Set-ItemProperty -Path $base -Name EnableHttp2Tls -Value 1 -Type DWord
Set-ItemProperty -Path $base -Name EnableHttp2Cleartext -Value 0 -Type DWord
# Restart HTTP.sys
net stop http /y
net start http
iisreset /restart
Inside IIS Manager, each site also has per-binding HTTP/2 control under the site’s Bindings node — the “Disable HTTP/2” checkbox. Leave it unchecked unless a specific backend app breaks on h2 stream multiplexing.
Step 2: enable HTTP/3 over QUIC
QUIC runs over UDP 443, which means a completely different firewall consideration from the TCP 443 you already have open. Two things must be true: the registry keys below, and UDP 443 allowed inbound.
# Enable HTTP/3 over QUIC (Windows Server 2022 build 20348+ / 2025)
$base = 'HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters'
Set-ItemProperty -Path $base -Name EnableHttp3 -Value 1 -Type DWord
Set-ItemProperty -Path $base -Name EnableQuic -Value 1 -Type DWord
# Allow QUIC through the firewall
New-NetFirewallRule -DisplayName "IIS HTTP/3 QUIC (UDP 443)" `
-Direction Inbound -Protocol UDP -LocalPort 443 -Action Allow -Profile Any
Restart-Service http -Force
- HTTP/3 is only negotiated after a browser has connected once over HTTP/2 — the Alt-Svc header tells it to try QUIC on the next request. Do not expect h3 on a cold first load.
- If your VPS provider filters UDP, HTTP/3 will silently fall back to HTTP/2. Verify with a packet-level or dev-tools check rather than assuming.
- Application Request Routing (ARR) and some WAF modules do not fully support QUIC; test reverse-proxy paths separately.
Step 3: the cipher suites both protocols require
HTTP/2’s RFC forbids cipher suites that browsers consider obsolete, and QUIC requires TLS 1.3 with AES-GCM or ChaCha20. If you removed TLS 1.3 or trimmed cipher suites too aggressively, HTTP/2 negotiation fails and browsers fall back to HTTP/1.1 without any obvious error. Confirm the state:
# Check TLS 1.2/1.3 and cipher suite order
Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -ErrorAction SilentlyContinue
Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Server' -ErrorAction SilentlyContinue
Get-TlsCipherSuite | Where-Object Name -match 'AES_GCM|CHACHA20' |
Select-Object Name, CipherSuitePosition
Verifying what the server actually negotiates
Server-side headers are not enough — you need to see the negotiated protocol. From a Windows box with the same TLS stack, use a quick TLS handshake probe, or from any machine use curl if available:
# From Linux/macOS or WSL: show negotiated protocol version
curl -sI --http2 https://example.com/ | head -1
# Confirm h3 is advertised
curl -sI https://example.com/ | Select-String -Pattern 'alt-svc'
In Chrome or Edge, open DevTools, go to Network, enable the Protocol column, and reload. You should see h2 for most requests and h3 once QUIC has been learned. If you only ever see http/1.1, the cause is almost always a missing TLS cipher or a TLS 1.2 minimum that your scanner report told you to set but that also removed the required AEAD suites.
Performance impact and when HTTP/3 is worth it
HTTP/3 pays off most for clients on lossy or high-latency links. Because QUIC replaces TCP’s head-of-line blocking with per-stream delivery, a single dropped packet no longer stalls every other request on the connection. On a clean low-latency LAN the gain is close to zero, which is why enabling it and measuring is the only honest approach.
| Scenario | HTTP/2 | HTTP/3 | Verdict |
|---|---|---|---|
| Many small assets, low latency | Excellent | Equivalent | No measurable gain |
| Mobile / lossy links | Head-of-line stalls | Per-stream recovery | Meaningful gain |
| High-latency satellite/VPN | Handshake cost per connection | 0-RTT resumption | Large gain |
| Server CPU constrained | Lower cost | Higher per-packet cost | Keep HTTP/2 |
Common failure modes
| Symptom | Likely cause | Fix |
|---|---|---|
| Only http/1.1 negotiated | TLS 1.2 ciphers trimmed below AEAD set | Re-add ECDHE+AESGCM suites |
| h2 works, h3 never | UDP 443 blocked or provider filters UDP | Open UDP 443; otherwise accept h2 |
| 500 on HTTP/2 only | Legacy ISAPI filter rejects streams | Disable HTTP/2 on that binding |
| Static files fine, app slow | Multiplexing exposes app serialization | Profile app, not IIS |
A final note on sizing: HTTP/3 adds a little CPU per connection because encryption is per-datagram, and QUIC connections live in kernel-side structures that need memory headroom. On a modest instance, compare the resource ceiling against what the host actually provisions — the tradeoffs are laid out in our Windows VPS comparison.



