Why Your Blazor Server SignalR Connection Keeps Dropping
Your Blazor Server app works perfectly in development. In production, users see "Attempting to reconnect..." overlays every few minutes, sometimes losing their work entirely. The browser console shows a stream of SignalR connection errors, and your logs are full of CircuitException messages. This is one of the most common production headaches for Blazor Server applications — and the fixes are well-understood once you know where to look.
What "SignalR connection dropping" actually looks like
Blazor Server holds a stateful connection between each user's browser and your server, called a circuit. The circuit transports UI diffs and user events over SignalR, which prefers WebSockets but can fall back to Server-Sent Events or Long Polling. When the SignalR transport breaks, the circuit dies, the user sees the reconnection overlay, and any in-memory component state is lost on reconnect.
Signs you're hitting this:
Browser console: Error: Connection disconnected with error 'Error: Server timeout elapsed without receiving a message from the server.'
Server logs: Microsoft.AspNetCore.SignalR.HubException or Circuit closed due to disconnect
Users report losing form data mid-input
Reconnect overlay appears every 1-5 minutes on inactive pages
The 8 causes, in order of frequency
- The reverse proxy is closing idle WebSockets
This is the most common cause we see. Many reverse proxies (older Nginx configs, default load balancers, some firewalls) close TCP connections that are idle for more than 60 seconds. SignalR's default keepalive interval is 15 seconds — but if the proxy doesn't honour WebSocket framing as activity, it'll cut the connection anyway.
Fix: Lower SignalR's keepalive frequency and configure your reverse proxy to allow long-lived WebSocket connections. In Program.cs:
builder.Services.AddSignalR(options =>
{
options.KeepAliveInterval = TimeSpan.FromSeconds(10);
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
});
On Adaptive Web Hosting's Blazor plans, IIS 10 is configured with native WebSocket support and an extended idle timeout specifically tuned for Blazor Server workloads — this failure mode is less common here than on generic Linux + Nginx setups.
- The CDN or proxy doesn't speak WebSockets
Some CDNs (Cloudflare's free tier in non-default configs, basic Akamai setups, generic AWS Application Load Balancers without WebSocket enabled) downgrade WebSocket upgrade requests back to HTTP/1.1 long-polling — or refuse them entirely. SignalR will silently fall back to Long Polling, which is slow, expensive, and tends to disconnect more aggressively.
Diagnostic: Open browser DevTools → Network → filter on WS. If you see Long Polling requests (HTTP requests to /_blazor?id=... every few seconds) instead of a persistent WebSocket frame, your transport got downgraded.
Fix: Enable WebSocket pass-through on your CDN/proxy. For Cloudflare, this is on by default on paid plans. For AWS ALB, enable "WebSocket support" in the target group settings. For Nginx, add the upgrade headers explicitly:
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
}
- The app pool is recycling under memory pressure
Blazor Server circuits live in the IIS worker process memory. Each circuit holds roughly 250 KB of state — component tree, event subscriptions, dependency-injected scoped services. On a busy app with 200+ concurrent circuits, that's ~50 MB just for circuit state. If your app pool's "Private Memory Limit" is set low (or to "0" which defaults to a hard cap on shared hosts), IIS recycles the worker, killing every active circuit.
Diagnostic: Check Event Viewer for IIS WAS events — "A worker process serving application pool [name] has requested a recycle because it reached its private bytes memory limit during its configured recycle time."
Fix: Raise the app pool memory limit, or upgrade to a plan tier with more headroom. Adaptive Web Hosting's Blazor plans are sized explicitly for Blazor Server workloads: Developer handles ~75 concurrent circuits, Business handles ~150-200, Professional ~300+.
- The TCP idle timeout on the load balancer
If your hosting sits behind an AWS NLB, GCP TCP load balancer, or a corporate firewall, the layer-4 device may have its own TCP idle timeout (often 60-350 seconds) regardless of what SignalR or IIS think. The WebSocket survives as long as either side sends a packet within that window, but a quiet user on a non-active page hits it.
Fix: Either set SignalR's KeepAliveInterval shorter than the TCP idle timeout (so SignalR sends keepalives before TCP gives up), or increase the load balancer's idle timeout. On AWS ALB the maximum is 4000 seconds; on most corporate firewalls you'll need to work with IT.
- Browser tab backgrounding throttles JS
Modern browsers throttle JavaScript timers in background tabs to save power. SignalR's heartbeat is on a JS timer. If the user leaves your app open in a tab they're not actively viewing, the browser may throttle the heartbeat to once per minute or worse. The server-side circuit timeout (default 30 seconds) expires before the next heartbeat fires.
Fix: Increase ClientTimeoutInterval on the server to match the worst-case browser throttle. 60 seconds is a reasonable production default:
builder.Services.AddSignalR(options =>
{
options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
options.KeepAliveInterval = TimeSpan.FromSeconds(20);
});
- The user's connection genuinely drops
Mobile networks, hotel Wi-Fi, corporate VPNs — these legitimately disconnect users occasionally. SignalR handles this automatically with reconnection logic, but the default UI shows the "Attempting to reconnect..." overlay during the reconnect attempt. For these short blips, the experience is acceptable; for longer outages, the circuit is gone and component state is lost.
Fix: Use a persistent state store (database, distributed cache like Redis) for any user-facing state that must survive disconnects. The Blazor Server pattern "keep critical state on the server, not in the circuit" is the standard remedy.
- A long-running Blazor event handler blocks the circuit
If a Blazor button click handler does a synchronous database query that takes 30 seconds, the SignalR circuit can't service heartbeats during that window. The client times out and the server thinks the user disconnected.
Diagnostic: Profile your slow event handlers. Any handler that does I/O should be async:
private async Task SubmitForm()
{
await _api.PostAsync(model); // good
// _api.Post(model); // bad — blocks the circuit
}
- Multi-server deployment without a backplane
If you run Blazor Server across multiple servers behind a non-sticky load balancer, every request might hit a different server. SignalR connections survive only as long as they hit the same server they originally connected to. Without sticky sessions or a backplane, every reconnect dies.
Fix: Configure sticky sessions on your load balancer, OR add a SignalR backplane (Redis is the simplest):
builder.Services
.AddSignalR()
.AddStackExchangeRedis("connection-string");
For single-server deployments (the common Blazor Server pattern on Adaptive Web Hosting), this doesn't apply.
The diagnostic flow we recommend
Open browser DevTools → Network → filter WS. If WebSocket is missing entirely, you have a transport problem (cause 2). If present, check the message frequency.
Look at server logs for Circuit closed due to disconnect. The reason field tells you whether the client gave up (network/throttling, cause 5/6) or the server killed the circuit (app pool recycle, cause 3).
Check Event Viewer for IIS WAS worker recycle events. If present, you've found cause 3.
If circuits drop after exactly N seconds of inactivity, you've found cause 1 or 4 — check your reverse proxy / load balancer idle timeout.
If specific user actions drop the circuit, profile the event handler for sync blocking I/O (cause 7).
Production-ready Blazor Server SignalR configuration
For most production Blazor Server apps, this configuration in Program.cs avoids the common gotchas:
builder.Services.AddSignalR(options =>
{
// Keepalive every 20s — under the 60s typical proxy idle timeout
options.KeepAliveInterval = TimeSpan.FromSeconds(20);
// Server gives up on the client after 60s — survives browser throttling
options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
// Allow larger messages for rich UIs
options.MaximumReceiveMessageSize = 64 * 1024;
});
builder.Services.AddServerSideBlazor(options =>
{
// Disconnected circuit kept warm for 3 min before garbage collection
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(3);
});
How Adaptive Web Hosting's Blazor plans address this
The most common SignalR drop causes on shared hosting are app-pool memory pressure and undersized reverse-proxy idle timeouts. Adaptive Web Hosting's Blazor Server hosting addresses both:
IIS 10 native WebSockets — enabled by default, no reverse-proxy layer to misconfigure
Dedicated IIS Application Pools — your worker process doesn't compete with neighbour tenants for memory; recycles are predictable
Plan-sized for Blazor circuit memory — Business plan handles 150-200 concurrent circuits comfortably, Professional ~300+
Real SQL Server 2022 — backplane-eligible if you outgrow single-server deployment
Plesk for Windows IIS log viewer — direct access to worker recycle events without RDP
Frequently asked questions
Why does my Blazor Server app reconnect every 30 seconds even with no user action?
Almost always a transport issue (cause 2) or reverse-proxy idle timeout (cause 1). Check the browser DevTools Network tab filtered to WS — if you see Long Polling requests instead of a persistent WebSocket frame, the transport was downgraded. Re-enable WebSocket pass-through on your proxy/CDN.
Can I switch to Blazor WebAssembly to avoid SignalR drops entirely?
Yes — Blazor WebAssembly doesn't use SignalR for the rendering loop. The trade-off is slower initial load (the .NET runtime downloads to the browser) and a different architectural pattern. See Blazor Server vs WebAssembly: Hosting Decision Guide for the full comparison.
What's the right ClientTimeoutInterval for production?
60 seconds is a safe default that survives browser tab throttling and most network blips. Higher than 60 means slower detection of genuine disconnects; lower than 30 means false positives on throttled or congested networks. The .NET 10 LTS default raised to 30s from .NET 8's 30s in recognition of common production patterns.
Does Adaptive Web Hosting support a Redis backplane?
Yes — the StackExchange.Redis NuGet package works on every Adaptive plan. You'd typically run Redis on a separate cloud service (Upstash, Redis Cloud, AWS ElastiCache) and connect from your Blazor Server app. For most single-server Blazor Server deployments, you won't need a backplane at all.
Why does the "Reconnecting..." overlay appear briefly even on healthy connections?
The default Blazor JS shows the overlay any time the client misses a heartbeat. If the user's network briefly drops a packet, SignalR retries within a second and the overlay vanishes. Customise the overlay's CSS to hide it for the first 2-3 seconds of disconnect — the brief flash is usually noise, not signal.
How do I tell if the issue is server-side or client-side?
If server logs show Circuit closed due to disconnect but no application-side errors, the client gave up. If server logs show a worker recycle event, the server killed the circuit. Cross-reference with the user's experience — if multiple users drop simultaneously, it's server-side; if one user drops repeatedly, it's their network or browser.
Bottom line
The vast majority of Blazor Server SignalR drops trace back to one of three causes: a reverse proxy not honouring WebSocket framing, app pool memory pressure recycling the worker, or browser tab throttling combined with too-short server timeouts. Each has a specific fix, and once they're addressed Blazor Server circuits stay stable for hours.
On Adaptive Web Hosting's Blazor plans, IIS 10 ships configured with native WebSocket support, your IIS worker has dedicated memory headroom, and the plans are sized for typical Blazor Server circuit loads. Every plan includes a 30-day money-back guarantee. View Blazor hosting plans or talk to an expert.