Blazor Server WebSocket Kestrel over HTTPS not upgrading to 101 on Windows 11 IoT LTSC clients as HTTP/2 is negotiated
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 290
Description
### Is there an existing issue for this?
- [x] I have searched the existing issues
### Describe the bug
We are observing a reproducible issue with Blazor Server / Server-Side Blazor running on Kestrel with HTTPS when accessed from Windows 11 IoT LTSC clients.
The Blazor WebSocket connection appears to be established from the browser perspective. The browser developer tools show the connection and the JavaScript logs report that the `wss://` connection is connected. However, the application behaves as if Blazor is not actually using a stable WebSocket transport. UI rendering and updates are extremely delayed, and the overall SignalR/Blazor experience is very unstable.
The issue only occurs when accessing the application via HTTPS from Windows 11 IoT LTSC clients. The same application works correctly when accessed from a regular Windows 11 Pro client.
## Environment
* Framework / Runtime: `.NET 10 x64`
* Application type: Blazor Web App / Server-Side Blazor
* Hosting: Kestrel process
* Protocol: HTTPS
* Server OS: Windows 11 IoT LTSC
* Affected client OS: Windows 11 IoT LTSC
* Working client OS: Windows 11 Pro, for example Windows 11 Pro 25H2
* Browsers tested on affected clients:
* Microsoft Edge
* Google Chrome
* Mozilla Firefox
* Reproduction application:
* Fresh/default Blazor Web App with Server-Side Rendering
* Only relevant change: HTTPS listener and certificate configuration for Kestrel
## Actual Behavior
When a Windows 11 IoT LTSC client accesses the Blazor Server application via HTTPS:
* The Blazor WebSocket request remains in status `200 OK`.
* It does not reach `101 Switching Protocols`.
* Browser logs still indicate that the `wss://` connection is connected.
* The page technically works, but UI updates and renderings are extremely delayed.
* The experience feels like Blazor/SignalR is falling back to another transport or otherwise not using a proper WebSocket connection.
* Server-side Blazor debug logs show that the `wss://` connection from Windows 11 IoT LTSC clients is being established using HTTP/2.
* The WebSocket upgrade never completes correctly.
## Expected Behavior
The Blazor WebSocket connection should negotiate correctly over HTTPS and complete the WebSocket upgrade with:
```text
101 Switching Protocols
```
The application should then behave the same as it does when accessed from a regular Windows 11 Pro client: stable SignalR connection, immediate UI updates, and normal Blazor Server performance.
## Working Scenario
When the exact same Blazor Server application is accessed from a regular Windows 11 Pro machine, for example Windows 11 Pro 25H2:
* The WebSocket negotiation happens over HTTP/1.1.
* The WebSocket request correctly receives `101 Switching Protocols`.
* Blazor Server works as expected.
* UI updates are fast and stable.
* The server can still run on a Windows 11 IoT LTSC machine, so the server itself seems not to be the issue
## Non-Working Scenario
When the exact same Blazor Server application is accessed from a Windows 11 IoT LTSC client:
* The WebSocket connection is attempted via HTTP/2 according to the Server debug logs.
* The request remains at `200 OK`.
* The connection does not upgrade to `101 Switching Protocols`.
* The page remains usable, but with severe delays and poor performance.
## Important Observation / Workaround
If Edge or Chrome is started on the affected Windows 11 IoT LTSC client with the following startup flag:
```text
--disable-http2
```
the issue disappears.
With HTTP/2 disabled in the browser on Windows 11 IoT LTSC:
* The WebSocket negotiation happens correctly.
* The connection receives `101 Switching Protocols`.
* Blazor Server / SignalR works smoothly.
* UI updates are fast and reliable.
This strongly suggests that the problem is related to WebSocket negotiation over HTTPS when HTTP/2 is used or selected on Windows 11 IoT LTSC clients.
## Steps to Reproduce
1. Set up a fresh Windows 11 IoT LTSC machine.
2. Run a Blazor Web App / Server-Side Blazor application on Kestrel with HTTPS enabled.
3. Use a standard Blazor Web App project with Server-Side Rendering.
4. Configure Kestrel with an HTTPS listener and certificate.
5. From another or from the same fresh Windows 11 IoT LTSC client, access the application via HTTPS using Edge, Chrome, or Firefox.
6. Open browser developer tools and inspect the Blazor WebSocket connection.
7. Observe that the WebSocket request stays at `200 OK` and does not upgrade to `101 Switching Protocols`.
8. Observe that complex UI rendering and updates are extremely delayed.
9. Check Blazor Server debug logs and observe that the `wss://` connection is being established using HTTP/2.
10. Repeat the same test from a regular Windows 11 Pro client.
11. Observe that the WebSocket negotiation happens over HTTP/1.1, the request correctly receives `101 Switching Protocols`, and the application works as expected.
12. On the Windows 11 IoT LTSC client, start Edge or Chrome with `--disable-http2`.
13. Access the same HTTPS endpoint again.
14. Observe that the WebSocket connection now upgrades correctly and Blazor works smoothly.
## Reproducibility
We were able to reproduce this consistently with multiple freshly installed test systems.
The issue is not specific to our production application. A fresh/default Blazor Web App with Server-Side Rendering and a minimal HTTPS Kestrel setup is sufficient to reproduce the behavior.
## Additional Notes
This issue may be related to: https://github.com/microsoft/aspire/issues/2406
A similar symptom was described there: Blazor WebSocket over HTTPS returning `200` instead of `101`.
However, in our case, the key missing detail appears to be the client operating system. We can reproduce the issue specifically with Windows 11 IoT LTSC clients. Regular Windows 11 Pro clients do not show the same behavior.
## Question
What logs, traces, or additional diagnostics would be most useful for the ASP.NET Core / Kestrel / SignalR team to investigate this?
### Expected Behavior
_No response_
### Steps To Reproduce
_No response_
### Exceptions (if any)
_No response_
### .NET Version
_No response_
### Anything else?
_No response_
Contributor guide
Research direction
Start with a fresh/default Blazor Web App using Server-Side Rendering and an HTTPS Kestrel listener, then reproduce from Windows 11 IoT LTSC and Windows 11 Pro clients. Compare browser developer tools and Server debug logs with HTTP/2 enabled and with Edge or Chrome started using --disable-http2; done means identifying why the affected path remains 200 OK instead of reaching 101 Switching Protocols.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend-api-design, networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100