Skip to main content

Command Palette

Search for a command to run...

Server-Sent Events vs SignalR vs WebSockets in ASP.NET Core: Which Real-Time Technology Fits Your .NET Team?

Updated
β€’12 min read
Server-Sent Events vs SignalR vs WebSockets in ASP.NET Core: Which Real-Time Technology Fits Your .NET Team?

Real-time communication has become table stakes for modern web applications. Whether you are building a live dashboard, a collaborative editor, a notification feed, or a financial ticker, your team will eventually reach for one of three technologies in the ASP.NET Core ecosystem: Server-Sent Events (SSE), SignalR, or raw WebSockets. Choosing the wrong one does not just mean a refactor β€” it means carrying operational complexity, scaling debt, and infrastructure cost that compound over the life of your product.

🎁 Want implementation-ready .NET source code you can drop straight into your project? Join Coding Droplets on Patreon for exclusive tutorials, premium code samples, and early access to new content. πŸ‘‰ https://www.patreon.com/CodingDroplets

ASP.NET Core 10 changed the calculus here by introducing a native, high-level SSE API through Results.ServerSentEvents. For the first time, teams can reach for SSE without hand-rolling raw response streaming, making it a genuinely competitive option alongside SignalR and WebSockets for server-push scenarios. This guide gives you a decision framework β€” not a tutorial β€” so your team can pick the right tool for the right job.

Understanding the Three Technologies

Before the comparison, it helps to be precise about what each technology actually is, because the marketing language around "real-time" obscures meaningful architectural differences.

Server-Sent Events (SSE) is a W3C standard (WHATWG spec) for unidirectional, server-to-client data streaming over a plain HTTP connection. The server sets the Content-Type: text/event-stream header and keeps the response stream open, pushing named events whenever data is available. Browsers handle automatic reconnection natively through the EventSource API. In .NET 10, Results.ServerSentEvents wraps IAsyncEnumerable<T> into a compliant SSE response with no infrastructure dependencies.

SignalR is a Microsoft-authored abstraction layer that selects the best available transport β€” WebSockets when available, then SSE, then HTTP long-polling β€” at connection time. It exposes a Hub-based RPC model where both server and client can invoke named methods on each other. SignalR handles group management, connection lifecycle, and serialization. Scaling beyond a single server requires a backplane (Redis Streams is the default choice) or Azure SignalR Service.

Raw WebSockets is the underlying full-duplex TCP-level protocol (RFC 6455) that SignalR can use under the hood. ASP.NET Core exposes WebSockets directly via HttpContext.WebSockets.AcceptWebSocketAsync(). You get a raw bidirectional byte channel and full control β€” and full responsibility β€” for framing, routing, reconnection, and scaling.

How Does SSE Differ From SignalR Under the Hood?

This is one of the most common questions developers ask. SignalR can use SSE as one of its fallback transports, but when you use SSE directly in .NET 10, you bypass the SignalR Hub protocol, client-side library requirements, and backplane dependency entirely. The result is a pure HTTP streaming endpoint: stateless from the infrastructure's perspective, horizontally scalable without a backplane, and compatible with any HTTP client including curl, fetch, and browser EventSource.

Side-by-Side Comparison

Dimension Server-Sent Events SignalR Raw WebSockets
Communication direction Server β†’ Client only Bidirectional Bidirectional
Protocol HTTP/1.1, HTTP/2 WS / SSE / Long-poll (negotiated) WebSocket (RFC 6455)
Client library needed No (browser EventSource native) Yes (@microsoft/signalr) No (browser WebSocket native)
Backplane for scale-out Not required Required (Redis, Azure) Required if state is shared
Auto-reconnect Yes (browser handles it) Yes (SignalR client handles it) Manual implementation
Message format Text (JSON or plain) MessagePack or JSON Any (text or binary)
Hub/RPC model No Yes No
Firewall / proxy friendliness High (plain HTTP) Medium (WS upgrade may be blocked) Medium (WS upgrade may be blocked)
.NET 10 native API Yes (Results.ServerSentEvents) Yes (mature) Yes (mature)
Operational complexity Low Medium–High High
Binary streaming No Yes (MessagePack) Yes

When Should You Use Server-Sent Events?

SSE is the right default for unidirectional, server-driven push scenarios where clients consume events but do not respond back. The strongest signal that SSE fits: you can describe your use case as "the server pushes updates, clients listen."

SSE is the strongest choice when:

  • Notification feeds β€” order status updates, system alerts, build pipeline events. The client only needs to receive; no client-to-server message is required.
  • Live dashboard metrics β€” CPU graphs, queue depths, sales counters. SSE handles this with zero client library overhead.
  • AI streaming responses β€” the pattern where a language model streams tokens back to the browser. Nearly every major LLM API uses SSE for this exact reason.
  • Audit and activity streams β€” compliance dashboards where events flow from the server to a monitoring view.
  • Low-ops environments β€” teams where adding a Redis backplane or Azure SignalR Service is not justified. SSE scales horizontally with stateless HTTP load balancing.
  • HTTP/2 multiplexing β€” SSE over HTTP/2 allows multiple event streams on a single TCP connection, addressing the historical HTTP/1.1 browser connection limit problem that plagued SSE before HTTP/2 adoption.

The .NET 10 Results.ServerSentEvents API accepts any IAsyncEnumerable<T> and handles all the streaming mechanics. Combined with System.Threading.Channels for internal message routing, this is a very capable, infrastructure-light pattern.

When Should You Use SignalR?

SignalR earns its complexity budget when you need bidirectional, real-time messaging with a structured RPC model and the team benefits from the abstraction it provides.

SignalR is the right choice when:

  • Collaborative editing β€” multiple clients send and receive updates. Google Docs-style concurrent editing. The Hub model lets you broadcast to groups, individuals, or all clients trivially.
  • Live chat β€” users send messages to the server and receive messages from others. The bidirectional Hub RPC model maps naturally here.
  • Multiplayer game state β€” high-frequency bidirectional messages where both client inputs and server state deltas flow simultaneously.
  • Client-to-server commands β€” scenarios where the client needs to invoke server methods (trigger a workflow, acknowledge an event, submit input) not just receive data.
  • Teams already using Azure SignalR Service β€” if scaling infrastructure is already in place, the cost of SignalR's complexity is already paid. Switching to raw SSE or WebSockets gains little.
  • Mixed client environments β€” when some clients cannot upgrade WebSocket connections (corporate proxies, older infrastructure), SignalR's fallback negotiation is a genuine operational advantage.

The key architectural consideration: SignalR's Hub connections are stateful. The server maintains connection state per client. This is powerful β€” it enables group broadcasts, connection-level identity, and Hub method invocation β€” but it mandates a backplane the moment you deploy more than one server instance.

When Should You Use Raw WebSockets?

Raw WebSockets belong in a narrow set of use cases where maximum control over the wire protocol is non-negotiable and your team is prepared to own the operational surface that comes with it.

Raw WebSockets are the right choice when:

  • Binary protocol requirements β€” you are implementing or integrating with a custom binary framing protocol (financial market data feeds, IoT telemetry, gaming protocols). SignalR's binary support via MessagePack covers many cases, but bespoke wire protocols require raw control.
  • Existing WebSocket client contracts β€” you are building the server side for a client that already speaks a specific WebSocket sub-protocol and cannot change.
  • Microsecond-level latency budgets β€” SignalR's Hub protocol and JSON/MessagePack overhead, while small, is measurable. For low-latency trading infrastructure or high-frequency IoT, raw WebSockets eliminate the extra serialization layer.
  • Full-duplex streaming with custom flow control β€” when you need to interleave multiple logical channels on a single WebSocket connection with your own framing.
  • Minimal dependency surface β€” embedded systems, edge workloads, or security-constrained environments where pulling in the SignalR client library is not acceptable.

The trade-off is real: with raw WebSockets you own reconnection logic, message framing, error recovery, group fanout, and backplane design. These are not trivial engineering investments.

Is There a Clear Winner?

Yes β€” for the majority of enterprise web application use cases in 2026, SSE is the underused default that teams should reach for first.

The reasoning: most "real-time" features in enterprise applications are actually unidirectional. Dashboards push data. Notifications push alerts. Progress indicators push status. Order tracking pushes state. For all of these, SSE delivers the outcome without requiring a client library, a backplane, sticky sessions, or connection state management.

SignalR becomes the right answer the moment bidirectional communication is required. Its Hub model genuinely simplifies client-to-server messaging and group broadcasting. The backplane requirement is a fixed cost that pays for itself when collaboration features or multi-sender messaging are part of the design.

Raw WebSockets should be a deliberate, justified decision β€” not a default. Teams that reach for raw WebSockets without a specific reason tend to rediscover why SignalR was built.

Real-World Scenarios Decision Matrix

Scenario Recommended Choice Reason
Streaming AI token output SSE Server β†’ client only, no backplane needed
Live metrics dashboard SSE Unidirectional, HTTP-native, easy scaling
Order / notification feed SSE Event-driven, reconnect-resilient
In-app chat feature SignalR Bidirectional, group broadcast, Hub RPC
Collaborative whiteboard SignalR Multi-client sync, bidirectional events
Custom binary feed (IoT) Raw WebSockets Binary protocol, no overhead
Financial market data Raw WebSockets or SSE Depends on directionality and volume
Admin live activity log SSE Read-only stream, low complexity
Multiplayer game Raw WebSockets or SignalR Depends on whether Hub abstraction helps

Common Anti-Patterns to Avoid

Anti-pattern 1: Defaulting to SignalR for everything. Teams that discover SignalR first often use it for notification feeds, live metrics, and AI streaming β€” all unidirectional use cases. The result is a Redis backplane being maintained for scenarios that plain SSE would have handled without infrastructure overhead.

Anti-pattern 2: Using raw WebSockets for "performance" without measuring. The performance difference between SignalR and raw WebSockets is negligible for the vast majority of enterprise throughputs. Before choosing raw WebSockets for performance reasons, profile first.

Anti-pattern 3: Polling instead of pushing. HTTP polling (repeated GET requests on a timer) is still common for dashboard-style features. SSE in .NET 10 is simple enough that the migration from polling to push has essentially no excuse threshold.

Anti-pattern 4: Missing the HTTP/2 opportunity with SSE. Deploying SSE over HTTP/1.1 with multiple browser connections per page can exhaust the browser connection limit. Ensure your ASP.NET Core host is configured for HTTP/2 (Kestrel default in .NET 10), and the problem disappears.

Anti-pattern 5: Scaling SignalR without a backplane plan. Teams that build SignalR features against a single-server dev environment sometimes discover the backplane requirement only when they add a second instance in staging. The architectural decision needs to happen before the first Hub is written.

β˜• Prefer a one-time tip? Buy us a coffee β€” every bit helps keep the content coming!

FAQ

Can I use Server-Sent Events with non-browser clients in ASP.NET Core? Yes. SSE is plain HTTP β€” any HTTP client that can read a streaming response can consume SSE events. In .NET, HttpClient with ResponseHeadersRead and stream-based reading works well. The native EventSource API is browser-specific, but the wire protocol is straightforward to consume from any language.

Does SignalR use WebSockets or SSE under the hood? SignalR negotiates the best available transport at connection time. It prefers WebSockets, falls back to SSE, and uses HTTP long-polling as a last resort. The transport used depends on both server configuration and what the client environment supports. You can constrain allowed transports in the SignalR options if you need consistency.

Does .NET 10 SSE work with HTTP/2? Yes. ASP.NET Core 10 SSE works over HTTP/2, which addresses the historical browser connection limit issue (HTTP/1.1 browsers limit connections per origin to 6). With HTTP/2 multiplexing, multiple SSE streams can share a single TCP connection. Kestrel supports HTTP/2 by default in .NET 10.

When does SignalR require a Redis backplane? Any time you deploy SignalR to more than one server instance (or container replica). Each server maintains its own in-memory connection registry. Without a backplane, a message sent via one server instance cannot reach clients connected to a different instance. Azure SignalR Service is an alternative to self-hosted Redis.

Is raw WebSocket support in ASP.NET Core production-ready? Yes, and it has been for several major versions. The raw WebSocket API in ASP.NET Core is mature and performs well. The trade-off is not stability β€” it is development complexity. You own all the protocol logic that SignalR handles for you.

Can SSE and SignalR coexist in the same ASP.NET Core application? Absolutely. They are independent features with no conflicts. A common pattern is using SSE for read-only streaming endpoints (metrics, notifications) and SignalR for interactive features (chat, collaboration) within the same application. Route them to separate URL prefixes and manage them independently.

What happens when an SSE client disconnects and reconnects? The browser EventSource API reconnects automatically after a configurable delay (default 3 seconds). On reconnection, it sends a Last-Event-ID header with the ID of the last event it received. The .NET 10 SseItem<T> type supports assigning event IDs so your server can resume from where the client left off. For critical event delivery, you still need to buffer events server-side.

Is there a performance difference between SSE and SignalR for high-throughput scenarios? For most enterprise throughputs (thousands of events per second), both perform well. SSE has lower per-connection overhead because it avoids the Hub protocol framing. For very high-frequency scenarios (tens of thousands of small messages per second), raw WebSockets with custom binary framing will outperform both. Profile your actual workload before choosing technology for performance reasons alone.

More from this blog

C

Coding Droplets

209 posts

Coding Droplets is your go-to resource for .NET and ASP.NET Core development. Whether you're just starting out or building production systems, you'll find practical guides, real-world patterns, and clear explanations that actually make sense.

From beginner-friendly tutorials to advanced architecture decisions. We publish fresh .NET content every day to help you grow at every stage of your career.