WebSockets Basics

Updated

September 8, 2026

WebSockets Basics

Overview

WebSockets upgrade an HTTP connection to a long-lived bidirectional channel. In Go, use golang.org/x/net/websocket (older) or the popular github.com/gorilla/websocket / nhooyr.io/websocket. This chapter shows a minimal gorilla-style echo and operational pitfalls.

Upgrade sketch (gorilla)

go get github.com/gorilla/websocket@latest
var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        // tighten in production
        return true
    },
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
}

func echoWS(w http.ResponseWriter, r *http.Request) {
    c, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        return
    }
    defer c.Close()
    for {
        mt, msg, err := c.ReadMessage()
        if err != nil {
            break
        }
        if err := c.WriteMessage(mt, msg); err != nil {
            break
        }
    }
}

Heartbeats

Idle proxies kill quiet connections. Send pings:

c.SetReadDeadline(time.Now().Add(60 * time.Second))
c.SetPongHandler(func(string) error {
    c.SetReadDeadline(time.Now().Add(60 * time.Second))
    return nil
})
// writer goroutine: c.WriteControl(websocket.PingMessage, ...)

Concurrency

One reader goroutine; one writer goroutine; never concurrent writers without a mutex or dedicated write pump.

read loop → app → write pump (single)

Auth

Authenticate on the HTTP upgrade (cookie/session or Sec-WebSocket-Protocol / query token). Don’t invent a second weak auth after upgrade without TLS.

When not to use WebSockets

Prefer WS Prefer HTTP/SSE
True bidirectional chat/games Server→client events only (SSE)
Low-latency duplex Simple request/response

Rules of thumb

Do Don’t
Limit message size Read unbounded frames
Ping/pong deadlines Assume connections live forever
Serialize writes Write from many goroutines bare

Try next

  1. Echo server + browser WebSocket client.
  2. Reject oversized messages.
  3. Broadcast hub with register/unregister channels.