We’re rapidly heading toward an event-driven world of data streams and APIs. Server-Sent Events fills a specific niche in this new world: an open, lightweight, subscribe-only protocol for event-driven data streams. This article explores how Server-Sent Events came to be, how it works under the hood, and why it’s rapidly being adopted by developers.
A bit of background
Server-Sent Events is based on something called Server-Sent DOM Events, which was first implemented in Opera 9. The idea is simple: a browser can subscribe to a stream of events generated by a server, receiving updates whenever a new event occurs. This led to the birth of the popular EventSource
interface, which accepts an HTTP stream connection and keeps the connection open while retrieving available data from it. The connection is kept open until closed by calling EventSource.close()
.
Realtime you can depend on. Ably allows you to easily build complete, simplified realtime applications that scale. Try our APIs for free.
What is Server-Sent Events (SSE)?
Server-Sent Events is a standard describing how servers can initiate data transmission towards clients once an initial client connection has been established. It provides a memory-efficient implementation of XHR streaming. Unlike a raw XHR connection, which buffers the full received response until the connection is dropped, an SSE connection can discard processed messages without accumulating all of them in memory.
Server-Sent Events is designed to use the JavaScript EventSource API in order to subscribe to a stream of data in any popular browser. Through this interface a client requests a particular URL in order to receive an event stream. SSE is commonly used to send message updates or continuous data streams to a browser client.
In a nutshell, a server-sent event is when updates are pushed (rather than pulled, or requested) from a server to a browser.
How does Server-Sent Events work?
A connection over Server-Sent Events typically begins with client-initiated communication between client and server. The client creates a new JavaScript EventSource object, passing the URL of an endpoint to the server over a regular HTTP request. The client expects a response with a stream of event messages over time.
The server leaves the HTTP response open until it has no more events to send, it decides that the connection has been open long enough and can be considered stale, or until the client explicitly closes the initial request.

Here’s a quick example of opening a stream over SSE:
var source = new EventSource('URL_TO_EVENT_STREAM');
source.onopen = function() {
console.log('connection to stream has been opened');
};
source.onerror = function (error) {
console.log('An error has occurred while receiving stream', error);
};
source.onmessage = function (stream) {
console.log('received stream', stream);
};
Server-Sent Events use cases
Ideally, when requesting data from a server, a simple XMLHttpRequest
will do. However, there are scenarios where you’d want to keep the connection open using XHR streaming. But this brings its own set of overheads, including handling parsing logic and connection management.
This is where Server-Sent Events comes in. SSE provides a layer of connection management and parsing logic, allowing us to easily keep the connection open while a server pushes new events to the client as they become available.
The nature of realtime messaging and streaming data mean different protocols serve different purposes better than others. For multiplexed, bidirectional streaming, WebSockets is perfect. For IoT devices with limited battery life, MQTT is more suitable. But sometimes these are overkill.
Server-Sent Events is perfect for scenarios such as:
When an efficient unidirectional communication protocol is needed that won’t add unnecessary server load (which is what happens with long polling)
When you need a protocol with a predefined standard for handling errors
When you want to use HTTP-based methods for realtime data streaming
When you need a unidirectional protocol with better latency for users than other HTTP-based ways of streaming data
Here’s a few examples where SSE is already in use:
Subscribing to a feed of cryptocurrency or stock prices
Subscribing to a Twitter feed
Receiving live sports scores
News updates or alerts
Server-Sent Events considerations
As with all things, SSE has its own challenges.
The major limitation of SSE is that it’s unidirectional so there’s no way to pass information to a server from a client. The only way to pass additional data is at the time of connection, which many developers choose to do with query strings. For example, if a stream URL is http://example.com/sse
, developers can add a query string to pass information such as a user id, e.g. http://example.com/sse?userid=891
.
This unidirectionality causes an additional problem: when a client loses a connection there’s no reliable way to let the server know as there’s no way to perform client-to-server heartbeats. As SSE is based on TCP/IP, there is a mechanism that alerts a server when a client loses a connection. But it doesn’t always work well so the server doesn’t always immediately realise a connection is lost. However, this is a minor limitation to SSE.
Server-Sent Events support
Server-Sent Events is widely supported across popular browsers – which in turn means it’s supported by a range of mobile and embedded IoT devices. That said, before attempting to implement SSE it’s worth checking support coverage for browsers your application or service supports.
Browsers that support SSE as of June 2019
It’s also worth checking EventSource
support. See the example below, which uses polyfill:
if ('EventSource' in window) {
// use polyfills
}
var source = new EventSource('URL');
A great example of polyfill for EventSource
is Yaffle’s EventSource polyfill.
Using Server-Sent Events at scale
Since Server-Sent Events is based on the HTTP protocol, scaling can be achieved with means such as load-balancing. However, you’d need to ensure some sort of shared resource behind the servers so they’re all kept in sync with new event updates.
Many choose to handle this with the publish/subscribe architecture design pattern. With pub/sub, events aren’t sent directly to clients but instead are sent to the broker. The broker then sends the message to all subscribers (which may include the original sender). To host your own pub/sub mechanism, you could consider using Redis.
If you’d rather offload this complexity of scaling and focus on your core product engineering, you can rely on realtime messaging platforms that offer event-stream and SSE endpoints. Check out this detailed explanation of how to implement SSE using a realtime messaging platform that supports the protocol.
How to use Server-Sent Events in the best way possible
While Server-Sent Events is easy to implement, often times developers tend to get stuck while sending data. SSE represents data in an actual stream, so should we need to send JSON with SSE, it looks something like this:
res.write('data: {\n');
res.write('data: "foo": "bar",\n');
res.write('data: "baz", 555\n');
res.write('data: }\n\n');
For developers used to writing pure JavaScript, when implementing SSE they might end up with invalid JSON while parsing on the client side. Using a library that abstracts this with plain JavaScript can help. For example, using the express-sse
library with Express as your web framework. Here’s an example of SSE from the server using Node’s Express:
app.get('/stream', (req, res)=>{
res.status(200).set({
"connection": "keep-alive",
"content-type": "text/event-stream"
});
res.write(`data: Hello there \n\n`);
});
Looking at the code above, we notice three things:
Text/event-stream content
type header. This is what browsers look for to confirm an event stream.Keep-alive
header. This tells the browser not to close the connection.The double new-line characters at the end of data. This indicates the end of the message.
However, looking at the example above, it’s only useful when sending one event or using the setInterval
function to continuously check the database for new connections.
If we were to use the express-sse
library, for example, it exposes a method where we can emit to the stream from anywhere in our app, and not necessarily the event stream route.
var SSE = require('express-sse');
var sse = new SSE();
app.get('/stream', sse.init);
// we can always now call sse.send from anywhere the sse variable is available, and see the result in our stream.
let content = 'Test data at ' + JSON.stringify(Date.now());
sse.send(content);
Server-Sent Events and Ably
Ably is an enterprise-ready pub/sub messaging platform. We make it easy to efficiently design, quickly ship, and seamlessly scale critical realtime functionality delivered directly to end-users. Everyday we deliver billions of realtime messages to millions of users for thousands of companies.
We power the apps people, organizations, and enterprises depend on everyday like Lightspeed System’s realtime device management platform for over seven million school-owned devices, Vitac’s live captioning for 100s of millions of multilingual viewers for events like the Olympic Games, and Split’s realtime feature flagging for one trillion feature flags per month.
We’re the only pub/sub platform with a suite of baked-in services to build complete realtime functionality: presence shows a driver’s live GPS location on a home-delivery app, history instantly loads the most recent score when opening a sports app, stream resume automatically handles reconnection when swapping networks, and our integrations extend Ably into third-party clouds and systems like AWS Kinesis and RabbitMQ. With 25+ SDKs we target every major platform across web, mobile, and IoT.
Our platform is mathematically modelled around Four Pillars of Dependability so we’re able to ensure messages don’t get lost while still being delivered at low latency over a secure, reliable, and highly available global edge network.
Try our APIs for free and see why Developers from startups to industrial giants choose to build on Ably because they simplify engineering, minimize DevOps overhead, and increase development velocity.
While SSE is a lightweight protocol from both an implementation and usage standpoint, there are still infrastructure considerations when operating event-driven streams at scale. And, as we continue to move toward a world of event-driven data streams, apps, services, and APIs, SSE won’t always be the right choice of protocol or fulfill your streaming needs. Now more than ever, building on a platform that can future-proof your infrastructure needs is something even the tech giants know is the best choice.
As such, here at Ably, we’ve always advocated for and supported open realtime protocols. By using Ably, developers can stream data using the open protocols of their choice, including Server-Sent Events, WebSockets, MQTT, STOMP, AMQP, and other proprietary realtime protocols, with minimal DevOps and infrastructure overheads.
Ably set a new gold standard for the type of service we expect from third parties. They were present as we got the system up and running, proactively notifying us of unexpected usage spikes indicating suspected bugs so we could take pre-emptive measures. On top of that we got great support and advice on ways we could use Ably’s realtime functionality to immediately optimize our product suite.

References and further reading on Server-Sent Events
Ably Engineering blog, which covers a wide spectrum of engineering challenges related to building a globally-distributed serverless realtime infrastructure at scale
Recommended Articles

Building apps with Xamarin and WebSockets: engineering challenges
Learn about the many challenges of implementing a dependable client-side WebSocket solution for Xamarin.

WebSockets and macOS clients: Hard engineering challenges
Learn about the many challenges of implementing a dependable client-side WebSocket solution for macOS to enable the delivery of realtime data.

WebSub – A deep dive
This article explores the history of WebSub, how it works, use cases (including big names who have adopted it), and the challenges of using WebSub.