Server-Sent Events in Go: When WebSocket is Overkill
Imagine: you're writing a dashboard for server monitoring. Data updates every 5 seconds. The client must receive notifications about load drops. What will you choose? WebSocket? Long Polling? What if I tell you there is a technology that works over plain HTTP, requires no additional libraries on the client, and is perfect for one-way data transmission?
Meet Server-Sent Events (SSE). It's a protocol built into HTML5 that allows the server to send data to the client in real time over a single HTTP connection. Unlike WebSocket, SSE works only in one direction — from server to client. But that's exactly what makes it ideal for notifications, logs, stock quotes, and any tasks where the server is the data source and the client is a passive listener.
In this article, we'll break down how to implement Server-Sent Events in Go. You'll learn how to create simple streaming, how to send custom events, and how to handle client disconnections. Let's go!
Why Go + SSE is Powerful?
Go is a language built for network applications. Its goroutines and channels fit the SSE model perfectly: each connection is a separate goroutine, and channels ensure safe data transfer. Plus, Go is a compiled language with excellent performance. You can hold tens of thousands of open SSE connections without significant memory costs.
The key advantage of SSE over WebSocket is simplicity. On the client side, the standard EventSource API is used, which works in all modern browsers. No additional libraries, no frameworks. The server side is a regular HTTP handler with the correct headers.
When should you use SSE?
- One-way transmission — the server sends data, the client only receives.
- Notifications and alerts — monitoring system, push notifications.
- Data streaming — logs, quotes, tweets, news.
- Automatic reconnection — the browser restores the connection on its own when it drops.
Basics of the Server-Sent Events Protocol
Before writing code, let's understand how SSE works at the HTTP level. When a client opens a connection, the server sends special headers:
Content-Type: text/event-streamCache-Control: no-cacheConnection: keep-aliveAfter that, the server sends data in the format:
data: Message
Each message is separated by two newlines. You can send multiple fields:
event: userlogindata: {"username": "alice"}
The event field defines the event type, and data defines its content (usually JSON). There are also id (last event identifier) and retry (reconnection time in milliseconds) fields.
Writing an SSE Server in Go
Let's start with the simplest — a server that sends the current time every second. Create a file main.go:
package main
import ( "fmt" "net/http" "time")
func main() { http.HandleFunc("/events", sseHandler) http.ListenAndServe(":8080", nil)}
func sseHandler(w http.ResponseWriter, r *http.Request) { // Set headers for SSE w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive")
// Create a flusher for immediate data sending flusher, ok := w.(http.Flusher) if !ok { http.Error(w, "Streaming unsupported", http.StatusInternalServerError) return }
// Infinite loop for sending events for { select { case <-r.Con