# How to Implement WebSocket Support in Gin: A Complete Guide

> Implement WebSocket support in Gin using `c.IsWebsocket()` and `c.Writer.Hijack()`. This guide details using Go libraries like gorilla for full-duplex communication with Gin features.

- Repository: [Gin-Gonic/gin](https://github.com/gin-gonic/gin)
- Tags: how-to-guide
- Published: 2026-02-27

---

**You implement WebSocket support in Gin by leveraging the framework's `c.IsWebsocket()` detection method and `c.Writer.Hijack()` capabilities to upgrade HTTP connections using specialized Go libraries like gorilla/websocket or coder/websocket, enabling full-duplex communication while preserving Gin's middleware and routing features.**

Gin is an HTTP-centric web framework that deliberately leaves the low-level connection upgrade step to the developer. While the gin-gonic/gin repository does not embed a native WebSocket server, it provides the essential plumbing in [`context.go`](https://github.com/gin-gonic/gin/blob/main/context.go) and [`response_writer.go`](https://github.com/gin-gonic/gin/blob/main/response_writer.go) to safely implement WebSocket support using third-party libraries.

## Why Gin Does Not Embed Native WebSocket Support

Gin maintains a strict **separation of concerns** by focusing exclusively on routing, middleware chains, and HTTP response rendering. WebSocket protocols involve complex variants—binary versus text frames, custom ping/pong handling, and sub-protocol negotiation—that are better served by specialized libraries.

By exposing the raw `net.Conn` through the standard `http.Hijacker` interface, Gin provides **maximum flexibility**. Developers can select any WebSocket library matching their performance requirements without framework bloat. This design also ensures **minimal impact on the request lifecycle**; the framework enforces that hijacking occurs only when no body data has been written, preventing corruption of partially-sent HTTP responses.

## Core Gin Mechanisms for WebSocket Upgrades

Three specific capabilities in the gin-gonic/gin source code enable clean WebSocket integration:

- **`c.IsWebsocket()`** — Detects client-initiated handshakes by checking the `Connection: Upgrade` and `Upgrade: websocket` request headers. Implemented in [[`context.go`](https://github.com/gin-gonic/gin/blob/main/context.go)](https://github.com/gin-gonic/gin/blob/master/context.go#L40-L47).

- **`c.Writer.Hijack()`** — Returns the underlying `net.Conn` along with a `*bufio.ReadWriter`, but only if no response body has been written. This safety guard is defined in [[`response_writer.go`](https://github.com/gin-gonic/gin/blob/main/response_writer.go)](https://github.com/gin-gonic/gin/blob/master/response_writer.go#L10-L20).

- **`c.Writer.WriteHeaderNow()`** — Forces immediate flushing of HTTP status and headers. Useful when you need to ensure the handshake response is sent before the WebSocket library writes its own frames. Located in [[`response_writer.go`](https://github.com/gin-gonic/gin/blob/main/response_writer.go)](https://github.com/gin-gonic/gin/blob/master/response_writer.go#L84-L90).

Together, these allow you to detect upgrade requests, run pre-upgrade middleware (authentication, rate-limiting), and then transfer control to a dedicated WebSocket library.

## Complete Implementation Examples

### Basic Echo Server with gorilla/websocket

The **gorilla/websocket** library accepts standard `http.ResponseWriter` and `*http.Request` parameters, making it the simplest integration path. It internally calls `Hijack()`, so you do not need to invoke it manually.

```go
package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool { return true },
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
}

func wsHandler(c *gin.Context) {
    if !c.IsWebsocket() {
        c.AbortWithStatus(http.StatusBadRequest)
        return
    }

    conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
    if err != nil {
        c.AbortWithError(http.StatusInternalServerError, err)
        return
    }
    defer conn.Close()

    for {
        typ, msg, err := conn.ReadMessage()
        if err != nil {
            break
        }
        if err = conn.WriteMessage(typ, msg); err != nil {
            break
        }
    }
}

func main() {
    r := gin.Default()
    r.GET("/ws", wsHandler)
    r.Run(":8080")
}

```

Key implementation details:

- Pass `c.Writer` and `c.Request` directly to `upgrader.Upgrade`.
- All Gin middleware executes normally before the upgrade occurs.
- No explicit `Hijack()` call is required because `gorilla/websocket` handles the connection takeover internally.

### Manual Hijack with coder/websocket

For libraries requiring direct `net.Conn` access, use Gin's explicit `Hijack` method. This pattern provides full control over the connection lifecycle.

```go
package main

import (
    "bufio"
    "context"
    "net"
    "net/http"

    "github.com/coder/websocket"
    "github.com/gin-gonic/gin"
)

func wsManualHijack(c *gin.Context) {
    if !c.IsWebsocket() {
        c.AbortWithStatus(http.StatusBadRequest)
        return
    }

    conn, rw, err := c.Writer.Hijack()
    if err != nil {
        c.AbortWithError(http.StatusInternalServerError, err)
        return
    }

    wsConn, err := websocket.Accept(conn, &websocket.AcceptOptions{
        OriginPatterns: []string{"*"},
    })
    if err != nil {
        conn.Close()
        return
    }
    defer wsConn.Close()

    for {
        typ, data, err := wsConn.Read(context.Background())
        if err != nil {
            break
        }
        if typ == websocket.MessageText {
            wsConn.Write(context.Background(), websocket.MessageText, data)
        }
    }

    rw.Flush()
}

func main() {
    r := gin.Default()
    r.GET("/ws", wsManualHijack)
    r.Run(":8080")
}

```

Critical considerations:

- `c.Writer.Hijack()` returns the raw `net.Conn` only when no body has been written, enforced by Gin's `responseWriter` implementation.
- You must manually close the hijacked connection and flush the buffered reader (`rw`) to clean up resources.
- After hijacking, the HTTP server no longer manages the connection, giving you full control over framing and sub-protocols.

### Generic Upgrade Helper Pattern

Create a reusable helper that separates Gin-specific validation from library-specific upgrade logic:

```go
func UpgradeIfWebsocket(c *gin.Context, upgrader func(http.ResponseWriter, *http.Request) (interface{}, error)) (interface{}, error) {
    if !c.IsWebsocket() {
        c.AbortWithStatus(http.StatusBadRequest)
        return nil, errors.New("not a websocket request")
    }
    return upgrader(c.Writer, c.Request)
}

```

Usage with any WebSocket library:

```go
r.GET("/ws", func(c *gin.Context) {
    conn, err := UpgradeIfWebsocket(c, func(w http.ResponseWriter, r *http.Request) (interface{}, error) {
        return upgrader.Upgrade(w, r, nil)
    })
    if err != nil {
        return
    }
    // Handle conn as *websocket.Conn
})

```

This pattern isolates the **Gin-specific** `IsWebsocket` check from library implementation details, improving code reusability across different WebSocket providers.

## Key Source Files for WebSocket Implementation

Understanding these specific files in the gin-gonic/gin repository helps debug upgrade issues:

| File | WebSocket Relevance |
|------|---------------------|
| [`context.go`](https://github.com/gin-gonic/gin/blob/main/context.go) | Contains `IsWebsocket()` implementation that checks `Connection` and `Upgrade` headers. |
| [`response_writer.go`](https://github.com/gin-gonic/gin/blob/main/response_writer.go) | Implements `http.Hijacker` with strict guards preventing hijacking after body writes. |
| [`README.md`](https://github.com/gin-gonic/gin/blob/main/README.md) | Documents general integration patterns for combining Gin with third-party Go libraries. |

## Summary

- Use **`c.IsWebsocket()`** from [`context.go`](https://github.com/gin-gonic/gin/blob/main/context.go) to validate WebSocket handshake requests before attempting connection upgrades.
- Access raw TCP connections via **`c.Writer.Hijack()`** in [`response_writer.go`](https://github.com/gin-gonic/gin/blob/main/response_writer.go), which enforces safe upgrade timing by blocking hijacking after response body data is written.
- Combine Gin's routing and middleware chains with specialized libraries like **gorilla/websocket** or **coder/websocket** for production-grade WebSocket services.
- Manage connection lifecycle manually after hijacking, including proper cleanup of buffered readers and explicit connection closing.

## Frequently Asked Questions

### Does Gin have built-in WebSocket support?

No. According to the gin-gonic/gin source code, the framework intentionally avoids embedding WebSocket protocols to maintain separation of concerns. Instead, it provides `c.IsWebsocket()` in [`context.go`](https://github.com/gin-gonic/gin/blob/main/context.go) and `c.Writer.Hijack()` in [`response_writer.go`](https://github.com/gin-gonic/gin/blob/main/response_writer.go), allowing seamless integration with dedicated WebSocket libraries while preserving Gin's middleware capabilities.

### Can I use Gin middleware with WebSocket connections?

Yes. Because the WebSocket upgrade occurs inside a standard Gin handler, all middleware—including authentication, rate limiting, and logging—executes normally before the connection hijacking. Once you call the upgrader or `c.Writer.Hijack()`, Gin releases the connection and the middleware chain terminates, transferring control to your WebSocket management code.

### What happens if I try to write to the response after hijacking?

Gin's `responseWriter` implementation in [`response_writer.go`](https://github.com/gin-gonic/gin/blob/main/response_writer.go) explicitly blocks HTTP body writes after hijacking to prevent protocol corruption. Attempting to use `c.String()`, `c.JSON()`, or similar methods after calling `c.Writer.Hijack()` will result in errors, ensuring the WebSocket handshake response remains intact and unmixed with HTTP content.

### Which WebSocket library works best with Gin?

The optimal choice depends on your specific requirements. **gorilla/websocket** integrates seamlessly with Gin's `http.ResponseWriter` interface without requiring manual `Hijack()` calls, making it ideal for rapid development. **coder/websocket** requires explicit `c.Writer.Hijack()` calls but offers a modern API with built-in context support. Both approaches are fully supported by the framework's architecture as implemented in the source code.