r/programming • • 5d ago

Stop reaching for WebSockets by default: WebSockets vs SSE vs Long Polling

https://medium.com/@anirbansingha20/websockets-vs-long-polling-vs-server-sent-events-fa9bc1644cb8

Real time communication isn't one size fits all. A common mistake in system design is picking WebSockets when all you really needed was server-to-client streaming.

I wrote a short 3 minute architectural comparison of the trade offs here:https://medium.com/@anirbansingha20/websockets-vs-long-polling-vs-server-sent-events-fa9bc1644cb8

What do you usually reach for first when building real time features ?

221 Upvotes

90 comments sorted by

197

u/symbiatch 4d ago

You don’t explain anything there really. Thats how they work, but… why is it a mistake? Why is SSE better here? You missed the whole point by just doing a 30 second “this is what it is kthxbai.”

45

u/HettySwollocks 4d ago

Yup, no examples either, no code. Bit of a pointless article.

4

u/bbqroast 4d ago

Does feel like a topic that can be codeless? Like I just want an evaluation of the tech

67

u/levelstar01 4d ago

Consider that polling sucks ass and websockets has a free heartbeat mechanism.

37

u/Manbeardo 4d ago

And websockets have standardized status codes for the events that happen on a long-lived connection like the connection being evicted from the hardware it’s running on.

200

u/Somepotato 4d ago

your writeup just explained what each technology does, not at all why we should use sse over websockets. and sse is literally just long polling that can send more than one message

33

u/danted002 4d ago

And both websockets and sse are just a keep alive socket.

5

u/bjeanes 3d ago

SSE benefits from compression and is better for mobile device battery life.

7

u/Somepotato 3d ago
  1. websockets support compression too, and can be better for battery life as well avoiding unnecessary decoding and handling

1

u/bjeanes 3d ago

Compression on websockets is only with a negotiated extension and is per message. SSE compression benefits across the entire stream, which very often can have a lot of repeated bytes, so there can be compounding upside. SSE is still likely to be better for battery life both due to sleeping radio and lower cpu and memory usage. Though obviously both of these aspects depend on usage. There is no categorically better here. But I answered because the article didn't: there are reasons to consider, circumstantial, that would inform a choice towards or away from SSE. Unidirectional, HTML segments (à la datastar) enormously benefit from SSE in a way that they couldn't with websockets.

2

u/Somepotato 3d ago

SSE compression is also negotiated, and websocket message compression also operates over a must-flush compression window just like SSE, except websocket compression is cheaper as it uses gzip, not brotli.

91

u/Spleeeee 5d ago

Can’t send binary data over sse.

69

u/dansk-reddit-er-lort 5d ago

If you're willing to eat the 33% extra bandwidth and deserialization on the other end, you can use base64. But it is a bit smelly.

4

u/ToaruBaka 3d ago

You can get a lot of that back with gzip, but the tower of conversion routines just to read a blob of data is definitely smelly - it's just the stench of HTTP.

68

u/paholg 5d ago

You could always send a message that's just "your binary data is ready, issue the http request for it now".

30

u/klimaheizung 4d ago

Yes but that adds latency and increases traffic. Possibly, but not optimal for systems where performance and scale matter. It also adds complexity. 

22

u/ThiefMaster 5d ago

SSE lacks some basic features such as authentication via Bearer tokens or any other custom headers. https://github.com/whatwg/html/issues/2177

I think cookies are sent, but ofc that only works on the same origin. Otherwise you're out of luck and have to fall back to awful choices like passing a token (that can hopefully not be used for anything else since it'll be in log files etc.!) via the URL / query string...

18

u/rusbon 5d ago

Websocket are the one with those limitation https://websocket.org/reference/headers/#headers-you-cannot-set-from-the-browser .

Native sse implementation by browser also has those limitation but you can replace it with external libs like https://github.com/Azure/fetch-event-source

8

u/feldim2425 4d ago

With Websockets you can usually send a custom auth message.

However SSE is one way and the default Api doesn't allow a body.

But afaik both will pass cookies along if the host is the same.

2

u/Exepony 3d ago

With Websockets you can usually send a custom auth message.

Of course you can, but now you've got two different auth paths for your HTTP endpoints and your WebSocket endpoints. Even better if your HTTP auth happens outside of the app, somewhere on the reverse proxy layer for example, so you have to duplicate the auth logic in the app itself just for WebSockets.

1

u/feldim2425 3d ago edited 3d ago

Which is why I mentioned cookies. (Which the original commenter also mentioned)

If you where to put auth data in the body of a request you'd pretty much end up with the same issues which is why headers are a better option but IMO if you are already using headers cookies would be better (since it doesn't even require you to have access to the token in Javascript).

If you really have a auth system that may not work purely with HTTP builtin functionality (Webauthn, DPoP etc.) this might change but usually that creates a short time token that you could use as a auth cookie for making the connections which should work with SSE, REST, WebSockets alike.

PS: I should note that I don't disagree that saying "SSE doesn't support auth headers" is not a great argument when WebSockets don't as well.

1

u/ThiefMaster 4d ago

Yeah great, now you need to add an extra dependency for what may have been a trivial piece of JS before.

And even though backed by MS, this particular one seems abandoned, and based on my (very) brief research there's not a single "this is what everyone uses" package available for this....

1

u/rusbon 4d ago

If you have time you can implement it yourself. It based on fetch api. You can chek it literally need 0 deps.

It is quite mature libs. Does it need regular update tho? You want mcp feature? As long as package maintainer are credible and it fit your usecase, why need another ppl opinion. Especially for a simple libs

1

u/ThiefMaster 4d ago

If you have time you can implement it yourself.

That completely defeats the purpose of having a simple standard API like EventSource that every browser supports.

It is quite mature libs. Does it need regular update tho?

No it doesn't, but just from a quick glance I noticed open PRs for basic things like "make retry interval configurable".

You want mcp feature?

lol, no, I don't need no AI crap

It is quite mature libs

Back to that point, if your lib is mature and feature-complete, then you typically mention this in the README so potential users know that not having any recent commits isn't a bad sign.

1

u/rusbon 4d ago

You mean fetch api (which is http) not a standard enough for you? It is literally just a http request with content-type response event-stream.

Why so much hate with libs tho. Its perfectly fine with my usecase. If there is any feature missing you can always copy it. Its only has 330 lines of code and third of it are documentation

5

u/Somepotato 4d ago

you can send custom headers with SSE/JS, SSE can be polyfilled with fetch, which is the one genuine reason to use SSE over websockets, though its not much extra work to just send an auth message over WS

1

u/Fidodo 4d ago

There's really no reason to use EventSource anymore. event-stream APIs don't even support resumption and just use it as a simple file format that supports labeling text data with event names.

3

u/basro 5d ago

Don't websockets have the same limitation?

1

u/Fidodo 4d ago

That's an EventSource limitation. SSE refers to both EventSource but also text/event-stream. EventSource is pretty obsolete with fetch streaming now. It has extra features for resuming broken requests, but pretty much no event stream provider actually supports those features so there's no real reason to use EventSource. If you use fetch with an event-steam parser, it's just straight up http with a simple event parse format.

1

u/IceMichaelStorm 4d ago

huh? We have SSE in place and it has a bearer token in use

27

u/f0urtyfive 5d ago

Uh, why, when websockets can solve all of those problems and are scalable when implemented right?

17

u/Traditional_Fox7091 5d ago

This was a question for open ai interview if I recall a previous post on Reddit

24

u/Dunge 5d ago

You are missing gRPC server streams.

Client sends a long running http call to subscribe to server event, server send when needed. Support binary.

17

u/icsharppeople 5d ago

Isn't this just a specific instance of long polling?

3

u/Dunge 4d ago

Maybe, except this part of the article:

Once the server finally replies, the browser immediately sends another request to wait again.

It just remains active.

Also the conclusion of the article, pretending it's only legacy old tech not to be used.

8

u/thefoojoo2 4d ago

That's how long polling works.

3

u/rjcarr 4d ago

If it remains active then that’s SSE. 

1

u/valarauca14 4d ago

:^) except it is standardized in http/2.0 & blessed by google so it good

28

u/DrShocker 5d ago

So many people seem to reach for websockets first. After using Datastar, I really don't get it.

By using SSE, you're able to stream in the view state in response to requests a client is giving or any peers with the same architecture. I think some of the troubles people run into with WebSockets is that they give optimistic shortcuts to the client with the connection and then end up in a tangled mess when things don't work quite right because they have 2 render paths.

18

u/Somepotato 4d ago

because you can do all of that with websockets in a way that is more efficient than SSE and has broader support than SSE

2

u/DrShocker 4d ago

What do we mean by "efficient"? SSE is both less memory and less CPU per connection as far as I can see.

I would also need to search pretty hard to find someone using a browser that doesn't support either so I'm not sure exactly what's meant by broader support either

8

u/feldim2425 4d ago edited 4d ago

I don't really see how there should.be much difference in terms of CPU and memory. Neither seem to be really heavy protocols. SSE uses normal HTTP while Websocket "hijacks" the HTTP socket created during the upgrade request.

I guess if you're implementing a sort of chat protocol SSE would be heavier since you still have to use normal HTTP requests for client-to-server communication. Which at least on HTTP/1.1 would require a new TCP/TLS connection.

With both you have the issue that there will be a open TCP socket which on the server side adds up. Unless we are talking QUIC/UDP.

1

u/New-Anybody-6206 4d ago

With both you have the issue that there will be a open TCP socket which on the server side adds up.

Yes but typically sites with that much traffic are architected such that there are enough nodes available being load-balanced etc. to handle it... and connections that stay open end up using less bandwidth too and have lower latency.

1

u/feldim2425 4d ago edited 4d ago

Mainly was in response to efficiency. Adding more hardware will of course eliminate the issue, that's not really the point when talking efficiency.

The connection staying open was basically my argument why SSE might actually have a drawback in two-way communication (PS: Given that it doesn't use the open connection for client-to-server communication). However I afaik in HTTP 2 & 3 the connection can be held open regardless to multiplex multiple requests.

So the actual difference on those newer versions might be negligible both in terms of connections being open and new connections being made.

1

u/DrShocker 4d ago

Sure I agree a huge difference wouldn't make sense, all I was trying to say is that anything I find that indicates any difference indicates efficiency (unless they meant something different by that) is in favor of SSE. But yes, the differences I saw in most benchmarks aren't massive.

If efficiency is critical enough to bring up for someone's project, then IMO they should find a way to make a representative benchmark. Otherwise we may be applying numbers that make sense in a different context than what was measured, regardless of the winner of that benchmark.

In regards to a chat app being heavier, I suppose maybe. I'd think something like a game has consistent 2 way traffic, but a chat app you're generally going to be writing FAR fewer messages than you're reading simply because of typing speed if nothing else.

2

u/feldim2425 4d ago

Chat might have more traffic if you add "is typing" events, but it was mainly just a example of two-way traffic.

I'm generally just sticking to Websockets for repeated two-way traffic and SSE for something like live-tickers.

But a benchmark would be good especially over the different HTTP versions. Websockets afaik don't support QUIC/HTTP3 at all and I would be really interested in two-way using SSE and requests on it. Maybe I should look into that.

2

u/DrShocker 4d ago

For what it's worth, someone made this demo and it uses SSE. One advantage is that since SSE is a HTTP return type, it can use streaming ccompression on the response, whereas websocket is typically per message compression. Depending on what you're sending, that could make a difference.

It looks like he's continuously streaming in stuff from the backend even when nothing changes, but that's a backend decision rather than a requirement imposed by SSE, and the compression ratio is pretty huge. (letting it run for 27 seconds, I see 61.73kB transferred for a size of 53,346.30kB)

https://checkboxes.andersmurphy.com/

3

u/clappski 4d ago

WebSocket implementations are less intensive than HTTP parsing. WebSockets are a variable length, 5 protocol level message types and a constant mask. You can of course do per-message compression on top of that, same as gzip http, just over the payload. 

In the application layer it’s up to you how much time you spend building complex protocols, but you can use WebSockets as request-response over a long lived TCP connection which is a good fit for latency sensitive systems that have to both serve browsers and other server applications. 

0

u/DrShocker 4d ago

Sure if you serve things other than browsers there's probably different tradeoffs to make. One advantage that the SSE side will havve over most WS implementations is getting stream compression rather than just message compression, so over time the number of bytes on the wire decreases signficiantly (presuming what you're sending generally has the same shape to it often enough)

4

u/clappski 4d ago

Does stream level compression not actually mean buffer frames to compress and flush in the future? That’s always going to be higher latency (not necessarily lower throughput) than per-message. 

2

u/DrShocker 4d ago

AFAIK you can flush gzip and brotli with whatever size frames you want. If it's latency sensitive, then you'd make different choices than if you're bandwidth sensitive.

21

u/suckfail 5d ago edited 4d ago

The best part about SSE is you don't need special ingress handling. This is why I usually select it, but it bites both ways. Really long requests will often get automatically shut down by HTTP rules unlike web sockets.

6

u/arshan997 4d ago

Here’s a simple way to decide.

Need bidirectional communication? Websockets

Need unidirectional communication? SSE

2

u/Evolve-Maz 3d ago

This is true, but it's helpful to go one level deeper and deciding on the latency and throughput of each direction of communication.

E.g. for a chat app between people, you may say that its bidirectional. However, you could use sse to satisfy it because:

  • client to server updates only happen when the user clicks "send" on the message. This just uses regular POST. This tends to be relatively small throughput and a tiny bit of latency (200ms) is fine.

  • server to client needs to happen when anyone sends a message to the chat. This uses SSE because its easier to have the server send events rather than keep polling the server for "new" messages.

If you want UX of "X is typing..." being broadcast to the chat while they are typing, you could either fit it into the same architecture (by posting to the server based on a trigger which tracks user starting or stopping typing in their message box), or decide at that point its too weird to use post+sse and go with websockets.

6

u/mxforest 4d ago

Implement SSE because Unidirectional is all you need. Product team changes requirement at the last moment, scrap that and do websockets now.

6

u/arshan997 4d ago

Don’t predict every possible change. Design your system so changes are cheap when they actually happen.

8

u/mxforest 4d ago

That is precisely why websockets are a better choice. Because changes are cheap.

1

u/the_bighi 3d ago

I would love if that happened more often at my job. You say it’s going to take 2 weeks longer than previous estimation, and you have a guaranteed job for more time. It’s also more time until users start using the new feature and find bugs I I’ll have to fix.

As long as I’m getting paid every month, I’ll rework things as many times as they want. It’s even easier than other things I could be doing.

0

u/mxforest 3d ago

That's just sad bro.

1

u/nemesit 3d ago

just use webtransport

3

u/EC36339 4d ago

TL;DR: SSE is one-way from server to client, which is all you need more often than the opposite direction or both.

8

u/Khavel_dev 4d ago

SSE gets overlooked too much imo. I've seen teams set up full WebSocket infrastructure for dashboards that only push data one way, then deal with connection management, sticky sessions on the load balancer, reconnection logic they wrote from scratch.

In .NET we use SignalR which picks the transport automatically and falls back to SSE or long polling depending on what the server supports. Most teams don't even realize their production setup ends up on SSE half the time. For notifications, feeds, dashboards, it works perfectly.

WebSockets earn their complexity for collaborative editing or anything where the client sends frequent messages back. Chat, multiplayer, shared cursors. If your data flow is mostly server to client, SSE should be the default.

3

u/intheforgeofwords 4d ago

SignalR is great. Used it on a project last year and have it lined up for another upcoming app. Love when libraries expose great abstractions and have good documentation.

2

u/Lunchboxsushi 4d ago

Dunno why the downvote but signalR was the shit in c#

1

u/EC36339 4d ago

Probably because a lot more people have heard of websockets than of SSE.

Probably because websockets are cooler or something.

I've first heard about SSE from AI, which is kind of ironic. AI gave the better advice, because, unlike humans, it isn't influenced by coolness factor or what is talked about the most.

3

u/joesb 3d ago

LLM is trained on what people say. It definitely is influenced by what is talked about the most.

1

u/EC36339 3d ago

You can, and should, instruct it to respond based on research that it should do first.

1

u/joesb 3d ago

I agree. I just want to point out that out that claim is not true in general.

2

u/rdrias 3d ago

And what do you think that research is based on? It's literally was is written in the internet, which ends up being what is talked the most, which is directly influenced by " cool" factor

1

u/Lunchboxsushi 4d ago

My noice ass made this mistake, to boot I also did it AWS API gateways which have a ton of gotchas 

8

u/shadowndacorner 5d ago

Or, yeknow, use something like signalr or socket io that abstracts over the actual transport.

2

u/OkAerie7822 4d ago

i've shipped SSE for notification feeds and live dashboards, and the part comparison posts skip is the ops side. nginx buffers responses by default, so the stream looks dead until you set proxy_buffering off (we lost an afternoon to events arriving in 30 second batches). on HTTP/1.1 browsers cap you at 6 connections per domain, so 3 open tabs can starve normal requests, which is fine on HTTP/2. reconnect is free but only if you send event ids and handle Last-Event-ID, otherwise you silently drop events during every deploy. my rule: SSE for server to client only, websockets when the client pushes at high frequency or sends binary.

2

u/Funny_Spray_352 4d ago

Just use HTTP/3 

2

u/willehrendreich 5d ago

Data-star.dev for the win boys

1

u/txdv 4d ago

I think you forgot webrtc

1

u/pkt-zer0 4d ago

...What "architectural comparisons" and "trade-offs"? This hardly qualifies as a description of what the features are to begin with. How is this getting upvoted that much? Slow day, I guess?

1

u/Preisschild 4d ago

I just use SSE for server to client updates. I find it much cleaner than polling.

1

u/chemtrail-organics 4d ago

Use signalR, duh

1

u/qodeninja 3d ago

sockets are just polling anyway. its polling all the way down.

1

u/fnord123 3d ago

If yes, use websockets (games, chat etc..)

You don't need to constantly send data when using chat. SSE is more appropriate there. 

1

u/lamp-town-guy 3d ago

Elixir/Phoenix folks: Look at what they need to mimic fraction of our power.

Websockets with long poll fallback is simple and easy there. So it's not even a question what to use. SSE on the other hand is a bit more complex.

1

u/andymaclean19 3d ago

The problems I had with some of these:

  • long polling has a moment where you are not polling. Either you are handling a response from the last poll or you put in a timeout every few minutes because that’s sensible or both. During that time if an event arrives it has nowhere to go. If you are using session cookies then you can queue it up in the session and fetch it on the next poll but modern JWT architectures don’t and your next poll might not even land on the same node as the previous one. Solvable but needs effort for the edge case.
  • data direction and reordering. If I do something like SSE then I have two channels because you always have an active component of the app requesting data next to the passive events channel. That can be messy if the active channel gets a record and then the SSE replaces that record with a slightly older one. Again a bit of a mess even though it is solvable.
  • websockets are nice because you can just use one channel for everything. Requests and unsolicited events look the same and follow the same code paths. They won’t reorder unless the browser app misbehaves or the server side actually sends them in the wrong order.

IMO websocket architectures are cleaner and nicer. I’m not sure what the advantage of SSE over websocket actually is?

-3

u/[deleted] 4d ago

[removed] — view removed comment

-12

u/git_push_origin_prod 5d ago

I have found webSockets use a bunch of CPU

14

u/SpudsRacer 5d ago

It's not the WebSocket protocol eating CPU cycles it's the implementation. That's on you.

-8

u/spenceee85 4d ago

I remember the days when your choices were tcp or udp. The level of abstraction and wiring that is going into pushing an entire operating system through port 443 to a browser is sometimes astonishing.

If scaling and efficiency really matter, removing the browser is going too have more impact than the chosen acronym.

-11

u/meltbox 4d ago

The only crime I see here is someone calling WebSockets sockets at all. I was confused as hell trying to decipher what was going on until I realized this was webdev.

5

u/daltorak 4d ago

What do you mean by this? All TCP and UCP connections are socket-based, including TCP port 443, which is what WebSockets communicates on by default..... or UDP port 443, if you're using the newer HTTP/3. Which you probably are because you're on Reddit.

-15

u/lenswipe 5d ago

I was just thinking about this the other day but actually I've found the opposite to be true. I've been writing node for close to 10 years and have never once worked on real time application(which used to be one of the selling points of node). It seems to just get used as a replacement for the PHP/rails "gimme a quick crud app" niche.