Blazor Interactive Server applications depend heavily on SignalR.
When a component is running interactively on the server, the browser and ASP.NET Core maintain a real-time connection. User actions travel to the server, component state is updated there, and the resulting UI changes are sent back to the browser.
That makes SignalR configuration an important part of a Blazor application.
For a small application, the default settings are usually enough.
As the application grows, developers often need more control:
How long should an idle connection remain open?
What happens when authentication expires?
Should stateful reconnects be allowed?
How large can SignalR buffers become?
Which transports should be available?
How should connection timeouts be configured?
How can these settings be applied specifically to Interactive Server components?
Older approaches sometimes involved reaching into endpoint metadata or applying workarounds around the generated Blazor SignalR endpoint.
ASP.NET Core provides a cleaner option through ConfigureConnection on AddInteractiveServerRenderMode. This gives Blazor developers direct, strongly typed access to the underlying SignalR connection configuration for Interactive Server components.
This article walks through the configuration and explains when each setting is useful.
How Interactive Server Uses SignalR
With Interactive Server rendering, the component code executes on the server.
The browser is connected to the server through SignalR:
Browser
|
| User clicks button
v
SignalR connection
|
v
ASP.NET Core
|
| Component executes
v
Updated UI
|
v
SignalR
|
v
Browser
For example:
<button @onclick="Increment">
Count: @count
</button>
@code {
private int count;
private void Increment()
{
count++;
}
}
The Increment method runs on the server when the user clicks the button.
The browser doesn't download the entire application logic like a WebAssembly application would.
Instead, the server maintains the interactive component state and communicates changes over the SignalR connection.
That makes the connection itself an important part of application behavior.
Enabling Interactive Server Rendering
A typical Blazor Web App setup includes:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddRazorComponents()
.AddInteractiveServerComponents();
var app = builder.Build();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
The first part:
.AddInteractiveServerComponents();
registers the server-side services required for Interactive Server components.
The second part:
.AddInteractiveServerRenderMode();
configures the application to support Interactive Server rendering.
A component can then use:
@rendermode InteractiveServer
to enable interactive server rendering for that component.
Why Connection Configuration Matters
The default SignalR configuration is designed to work for a broad range of applications.
But production applications can have very different requirements.
Consider a dashboard with hundreds of connected users.
Each browser may maintain a long-running SignalR connection:
User 1 ────── SignalR ────── Server
User 2 ────── SignalR ────── Server
User 3 ────── SignalR ────── Server
...
User 500 ───── SignalR ────── Server
Now add:
Large messages
Slow networks
Authentication expiration
Temporary network interruptions
Multiple server instances
Reverse proxies
Stateful reconnects
Default settings may not always be ideal.
The important thing is to change settings for a specific reason rather than increasing every limit.
The ConfigureConnection API
For Interactive Server components, Blazor exposes:
ConfigureConnection
through AddInteractiveServerRenderMode.
A basic configuration looks like:
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(options =>
{
options.ConfigureConnection =
dispatcherOptions =>
{
// SignalR connection settings
};
});
The dispatcherOptions parameter gives access to HttpConnectionDispatcherOptions.
This is a much cleaner way to configure the underlying SignalR connection than trying to discover or modify endpoint metadata manually.
Configuring Authentication Expiration
One common problem with long-lived SignalR connections is authentication expiration.
Imagine a user signs in and receives an access token that expires after one hour.
The user opens a Blazor application:
09:00
User signs in
|
v
SignalR connection established
At:
10:00
the access token expires.
Without an authentication-refresh strategy, the existing connection may eventually close when the authentication credential expires.
.NET 11 adds SignalR authentication refresh support so a connected client can refresh authentication without dropping the connection.
For an Interactive Server connection, the underlying connection can also be configured to close when authentication expires:
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(options =>
{
options.ConfigureConnection =
dispatcherOptions =>
{
dispatcherOptions
.CloseOnAuthenticationExpiration = true;
};
});
This is particularly useful when you want the server to enforce the lifetime of an authenticated connection rather than allowing it to continue indefinitely.
Authentication Refresh in SignalR
ASP.NET Core 11 adds authentication-refresh support to SignalR connections.
For a regular SignalR hub, it can be enabled like this:
app.MapHub<ChatHub>("/chat", options =>
{
options.EnableAuthenticationRefresh = true;
options.CloseOnAuthenticationExpiration = true;
});
The server can also inspect the refreshed identity:
app.MapHub<ChatHub>("/chat", options =>
{
options.EnableAuthenticationRefresh = true;
options.OnAuthenticationRefresh =
context =>
{
// Validate refreshed identity.
return ValueTask.FromResult(true);
};
});
The server exposes a refresh endpoint alongside negotiation and reports token lifetime information so the .NET client can refresh authentication before the token expires.
There is an important limitation to remember: the .NET 11 implementation currently targets the .NET client; JavaScript/TypeScript client and Azure SignalR Service support were still in progress in the current release documentation.
For a Blazor Interactive Server application, always verify the exact client and hosting combination you use.
CloseOnAuthenticationExpiration
The option:
dispatcherOptions.CloseOnAuthenticationExpiration = true;
tells the connection infrastructure to close the connection when authentication expires.
This can be useful for applications where authentication lifetime is a strict security boundary.
For example:
User authenticated
|
v
SignalR connection
|
v
Authentication expires
|
v
Connection closes
Without a clear expiration policy, developers can accidentally create a long-lived connection whose authentication state no longer matches the user's current identity.
Whether you should enable this depends on how your authentication system handles refresh.
Allowing Stateful Reconnects
Network connections can fail temporarily.
A user might:
Switch Wi-Fi networks
Lose internet access for a few seconds
Put a laptop to sleep
Move between mobile networks
Experience a temporary proxy failure
A normal disconnect can force the client to reconnect and potentially rebuild its interactive state.
Stateful reconnects are designed to make short interruptions less disruptive.
For Interactive Server components:
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(options =>
{
options.ConfigureConnection =
dispatcherOptions =>
{
dispatcherOptions
.AllowStatefulReconnects = true;
};
});
This enables the underlying SignalR connection option for stateful reconnects.
The feature can be particularly useful for applications where users spend a long time on the same interactive page.
Why Stateful Reconnects Matter
Imagine a user filling out a long form:
Name
Email
Address
Company
Description
...
The browser temporarily loses network connectivity.
Without a suitable reconnect strategy:
Connection lost
|
v
Reconnect
|
v
Application state may need recovery
With stateful reconnect support:
Temporary disconnect
|
v
Reconnect
|
v
Recover buffered connection state
The exact behavior depends on the connection and application state, so stateful reconnects should not be treated as a replacement for durable application state.
If losing the circuit would cause important user data to disappear, save that data somewhere durable.
Stateful reconnect is a connection resilience feature, not a database.
Configuring ApplicationMaxBufferSize
Another setting exposed through HttpConnectionDispatcherOptions is:
ApplicationMaxBufferSize
For example:
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(options =>
{
options.ConfigureConnection =
dispatcherOptions =>
{
dispatcherOptions
.ApplicationMaxBufferSize =
1024 * 1024;
};
});
This controls the maximum amount of data buffered by the application side of the connection.
A larger buffer can be useful when your application legitimately sends larger messages.
But increasing it without understanding the traffic pattern can increase memory usage.
For example:
1 KB × 1,000 connections
is very different from:
1 MB × 1,000 connections
The second scenario can represent a significant memory footprint.
Do not increase buffer limits simply because a message failed once.
First identify why the message is large.
Large Messages Are Often a Design Problem
Suppose a component tries to send a very large object through the interactive connection.
You might be tempted to increase:
ApplicationMaxBufferSize
But sometimes the better solution is to change the application design.
Instead of:
Server
|
| 10 MB object
v
SignalR
|
v
Browser
consider:
Browser
|
| Request data
v
HTTP endpoint / download
|
v
Server
SignalR is excellent for interactive UI events and real-time updates.
It is not automatically the best transport for large file transfers.
Use the right mechanism for the type of data being transferred.
Configuring SignalR Timeouts
SignalR has several timeout-related settings.
The most important ones for server-side applications include:
ClientTimeoutInterval
HandshakeTimeout
KeepAliveInterval
Typical defaults are:
ClientTimeoutInterval = 30 seconds
HandshakeTimeout = 15 seconds
KeepAliveInterval = 15 seconds
The server sends keep-alive pings, and the client uses timeout settings to determine when a connection is considered unresponsive. Microsoft recommends keeping the timeout at least twice the keep-alive interval.
For example:
builder.Services
.AddRazorComponents()
.AddInteractiveServerComponents()
.AddHubOptions(options =>
{
options.ClientTimeoutInterval =
TimeSpan.FromSeconds(60);
options.HandshakeTimeout =
TimeSpan.FromSeconds(30);
});
The keep-alive interval can remain at its default of 15 seconds.
Why Keep-Alive Matters
Suppose a client suddenly loses its network connection.
The server may not immediately know that the client has disappeared.
Keep-alive messages help detect this situation.
Conceptually:
Server
|
| Ping
v
Browser
|
| Connection alive
v
Server
If the expected communication stops for long enough, the connection can be considered dead.
The keep-alive interval therefore affects both connection detection and network traffic.
A shorter interval means:
More pings
+
Faster detection
+
More network traffic
A longer interval means:
Fewer pings
+
Potentially slower detection
+
Less network traffic
Use values based on your actual application requirements.
The Two-Timeout Rule
A common mistake is changing one timeout without considering the others.
For example:
options.ClientTimeoutInterval =
TimeSpan.FromSeconds(20);
options.KeepAliveInterval =
TimeSpan.FromSeconds(15);
This gives only a small margin.
Microsoft's guidance recommends that the client timeout be at least twice the keep-alive interval.
A more comfortable configuration might be:
options.ClientTimeoutInterval =
TimeSpan.FromSeconds(60);
options.HandshakeTimeout =
TimeSpan.FromSeconds(30);
while keeping:
KeepAliveInterval = 15 seconds
The general relationship is:
Client timeout
>=
2 × Keep-alive interval
The exact value should still be chosen according to the application's environment.
Configure the Client Too
Server configuration alone is not always enough.
The browser-side SignalR configuration has corresponding settings.
For a Blazor application, client configuration can be customized through the Blazor startup configuration.
For example:
<script src="_framework/blazor.web.js"></script>
<script>
Blazor.start({
circuit: {
configureSignalR: function (builder) {
builder.withServerTimeout(60000);
builder.withKeepAliveInterval(15000);
}
}
});
</script>
The important part is that client and server values should be compatible.
For example:
Server KeepAlive
15 seconds
Client KeepAlive
15 seconds
Client ServerTimeout
60 seconds
This gives the connection enough room to tolerate normal network delays.
WebSockets Are Usually Preferred
Blazor Interactive Server applications use SignalR, and SignalR supports several transports.
The common options include:
WebSockets
Server-Sent Events
Long Polling
For server-side Blazor, WebSockets generally provide the best performance characteristics when available. Microsoft recommends WebSockets because of their lower latency and efficient real-time communication.
In normal deployments, let SignalR negotiate the appropriate transport unless you have a specific reason to restrict it.
If you need to configure transports for a custom hub:
app.MapHub<ChatHub>("/chat", options =>
{
options.Transports =
HttpTransportType.WebSockets |
HttpTransportType.LongPolling;
});
This kind of direct hub configuration applies to your own SignalR hub.
For Interactive Server components, use the Blazor ConfigureConnection mechanism for the connection options exposed there.
Be Careful With Long Polling
Long Polling can be useful when WebSockets are unavailable.
But it creates a different traffic pattern.
Instead of maintaining one persistent WebSocket connection:
Browser ───────────── Server
the client repeatedly sends requests:
Request
|
Response
|
Request
|
Response
This can increase request volume and server overhead.
If your production environment unexpectedly falls back to Long Polling, investigate why.
Common causes include:
Reverse proxy configuration
Load balancer restrictions
Firewall rules
WebSocket support being disabled
Incorrect infrastructure configuration
Do not automatically solve the problem by increasing timeouts.
First identify the transport being used.
Configuring the Interactive Server Connection
Putting several settings together:
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(options =>
{
options.ConfigureConnection =
dispatcherOptions =>
{
dispatcherOptions
.CloseOnAuthenticationExpiration = true;
dispatcherOptions
.AllowStatefulReconnects = true;
dispatcherOptions
.ApplicationMaxBufferSize =
1024 * 1024;
};
});
This gives the application direct control over the underlying connection settings.
The important thing is that these settings are configured specifically for the Interactive Server render mode rather than trying to discover the generated endpoint later.
Adding Hub Options
Some SignalR behavior is configured at the service level rather than through ConfigureConnection.
For example:
builder.Services
.AddRazorComponents()
.AddInteractiveServerComponents()
.AddHubOptions(options =>
{
options.ClientTimeoutInterval =
TimeSpan.FromSeconds(60);
options.HandshakeTimeout =
TimeSpan.FromSeconds(30);
});
Then:
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
This separation is useful:
Razor component setup
|
+-- AddInteractiveServerComponents
|
+-- AddHubOptions
|
v
Interactive Server
Endpoint setup
|
+-- AddInteractiveServerRenderMode
|
+-- ConfigureConnection
Use the configuration API that corresponds to the setting you actually need.
Connection Configuration Is Not Circuit State
This distinction is important in Blazor Server applications.
A SignalR connection carries communication between browser and server.
A Blazor circuit represents the interactive application state associated with that connection.
They are related, but they are not exactly the same thing.
For example:
Browser
|
v
SignalR Connection
|
v
Blazor Circuit
|
+-- Component state
+-- Scoped services
+-- UI state
If a connection drops, the circuit may be disconnected temporarily and can potentially reconnect depending on the configuration.
But you should not treat the SignalR connection as a durable storage mechanism.
Important application state should be stored appropriately.
Scaling Interactive Server Applications
A single server is easy:
Users
|
v
ASP.NET Core Server
A production environment may use:
Load Balancer
/ \
v v
Server A Server B
Interactive Server applications maintain server-side state, so scaling requires additional planning.
Microsoft's Blazor SignalR guidance notes that server-side applications commonly require session affinity, also called sticky sessions, when hosted in a server farm.
The reason is straightforward.
A user's circuit lives on a particular server.
If subsequent connection requests are sent to a different server that does not have the relevant circuit state, the application can experience connection problems.
Sticky Sessions
A common deployment pattern is:
User A
|
v
Load Balancer
|
v
Server A
and subsequent requests from that user continue going to Server A.
Another user might be assigned to Server B:
User B
|
v
Load Balancer
|
v
Server B
This is session affinity.
If you need to scale Interactive Server applications, make sure the load balancer and hosting infrastructure support the required affinity behavior.
Azure SignalR Service
Azure SignalR Service can be used to offload SignalR connection management from application servers.
The architecture becomes:
Browser
|
v
Azure SignalR Service
|
v
ASP.NET Core
This can be useful for applications with large numbers of concurrent connections.
However, you should still understand how Blazor circuit state and server-side component execution work.
A managed SignalR service does not magically turn Interactive Server into a stateless architecture.
Connection Limits and Memory
Every interactive connection consumes server resources.
That can include:
Connection state
Circuit state
Scoped services
Buffers
Component state
Timers
Event handlers
If an application has:
10 users
resource usage may be insignificant.
At:
10,000 users
the architecture becomes much more important.
This is why increasing buffer sizes or retaining circuits for longer periods should be done carefully.
For example:
More connections
+
Larger buffers
+
Longer circuit retention
=
Higher memory usage
Monitor memory under realistic concurrent load.
Do Not Solve Every Disconnect With Longer Timeouts
A common troubleshooting reaction is:
Connection drops
|
v
Increase timeout
|
v
Connection drops again
|
v
Increase timeout again
This can hide the real problem.
A disconnect might be caused by:
WebSocket failure
Reverse proxy timeout
Network interruption
Authentication expiration
Server restart
Memory pressure
Load-balancer behavior
Circuit termination
Client-side connectivity problems
Find the cause before changing timeout values.
Troubleshooting Interactive Server Connections
When users report that a Blazor application frequently disconnects, check the following.
Check the Browser Console
Look for SignalR connection messages and transport errors.
Check Server Logs
Look for:
Connection closed
Handshake failed
Authentication expired
Circuit disconnected
Transport failure
Check the Transport
Determine whether the connection is using WebSockets or falling back to Long Polling.
Check the Reverse Proxy
Verify that WebSocket upgrades are supported.
Check Timeouts
Compare:
KeepAliveInterval
ClientTimeoutInterval
Proxy timeout
Load balancer timeout
A proxy timeout shorter than the application's expectations can cause unexpected disconnects.
Check Authentication
If the disconnect happens at a predictable time, token or cookie expiration may be involved.
Security Considerations
Connection configuration also has security implications.
Authentication Expiration
Do not allow an authenticated interactive session to continue beyond the intended authentication lifetime without a deliberate refresh strategy.
Buffer Sizes
Larger buffers can increase memory usage and may expand the impact of malicious or accidental large messages.
WebSocket Compression
Interactive Server components enable WebSocket compression by default. Compression can improve network efficiency, but developers should consider the security implications when sensitive dynamic data is involved.
Authorization Still Applies
A SignalR connection being authenticated does not mean every action should be allowed.
Continue enforcing authorization at the appropriate application and component boundaries.
A Practical Production Configuration
A reasonable starting point for an application that needs longer-lived connections might look like:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddRazorComponents()
.AddInteractiveServerComponents()
.AddHubOptions(options =>
{
options.ClientTimeoutInterval =
TimeSpan.FromSeconds(60);
options.HandshakeTimeout =
TimeSpan.FromSeconds(30);
});
var app = builder.Build();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(options =>
{
options.ConfigureConnection =
dispatcherOptions =>
{
dispatcherOptions
.CloseOnAuthenticationExpiration = true;
dispatcherOptions
.AllowStatefulReconnects = true;
};
});
app.Run();
This does not mean these values are universally correct.
They simply demonstrate how the different configuration points fit together.
Tune them after measuring the actual application.
When Should You Change the Defaults?
Change the defaults when you have a clear requirement.
For example:
Requirement
|
v
Longer network interruptions
|
v
Evaluate stateful reconnect
Requirement
|
v
Authentication must expire strictly
|
v
Evaluate CloseOnAuthenticationExpiration
Requirement
|
v
Large legitimate messages
|
v
Evaluate ApplicationMaxBufferSize
Requirement
|
v
Slow startup environment
|
v
Evaluate HandshakeTimeout
Avoid changing settings simply because a blog post recommends larger values.
A configuration that works well for one application can be harmful for another.
Common Mistakes
Increasing Every Timeout
Longer timeouts are not automatically better.
Increasing Buffer Limits Without Measuring Memory
A larger buffer can increase memory consumption across many connections.
Assuming Stateful Reconnect Preserves All Application State
It helps with connection recovery, but it is not a replacement for durable storage.
Ignoring WebSocket Support
If the application unexpectedly uses Long Polling, investigate the infrastructure.
Forgetting Authentication Expiration
Long-lived interactive connections need an explicit authentication strategy.
Testing Only With One User
Connection problems often appear only under concurrency.
Ignoring the Load Balancer
Interactive Server applications need careful planning when deployed across multiple servers.
Treating SignalR as a File Transfer System
Use appropriate HTTP-based mechanisms for large downloads and uploads.
Production Checklist
Before deploying a large Interactive Server application, verify:
Interactive Server rendering is enabled intentionally.
WebSockets work through the production proxy.
SignalR timeouts are understood.
Keep-alive and timeout values are compatible.
Authentication expiration behavior is defined.
Stateful reconnects are enabled only when useful.
Buffer sizes match real message requirements.
Large data transfers use appropriate endpoints.
Load-balancer session affinity is configured where required.
Server memory is monitored under concurrent load.
Connection and circuit failures are logged.
Authentication and authorization are tested after reconnects.
Reverse proxy and load-balancer timeouts have been reviewed.
WebSocket compression has been considered from both performance and security perspectives.
Summary
Blazor Interactive Server applications depend on SignalR, so connection configuration becomes increasingly important as an application moves from a small project to a production system with many users.
ASP.NET Core provides a cleaner way to configure the SignalR connection used by Interactive Server components through ConfigureConnection on AddInteractiveServerRenderMode. This avoids endpoint-metadata workarounds and gives developers direct access to settings such as CloseOnAuthenticationExpiration, AllowStatefulReconnects, and ApplicationMaxBufferSize.
.NET 11 also adds SignalR authentication refresh, which is useful for long-running authenticated connections where access tokens can expire while the connection remains active.
The main lesson is not to change every SignalR setting just because the options are available. Timeouts, buffers, reconnect behavior, authentication lifetime, and transport configuration all affect server resources and user experience.
Start with the defaults, identify the actual problem, change the smallest number of settings necessary, and test the application under realistic network conditions and concurrent connections.
That approach gives you a more reliable Blazor application without turning SignalR configuration into a collection of arbitrary workarounds.

Join the conversation! Your thoughts help the community grow.