TLD(W)R;

// Allow everyone
var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        return true
    },
}


// Allow a specific domain
var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        origin := r.Header.Get("Origin")
        return origin == "https://your.domain.com"
    },
}

Long(er) version

When you are working with Gorilla WebSockets in Go and run into a CORS error, it might be tempting to to allow requests from any origin. Allowing all origins (i.e., *) can be convenient, especially during development, but it’s not always the best approach for production. Still, let’s walk through how to get it done.

Why Does Origin Matter?

Web browsers implement a security feature called Cross-Origin Resource Sharing (CORS). This means that a web page can only request resources (like WebSockets) from the same domain unless the server explicitly allows other domains to connect. In the case of WebSockets, if you’re serving from ws://your.domain.com, but your client is hosted on http://other.domain.com, the server will need to specify that it’s okay to allow that connection. That’s where Origin headers come into play.

By default, the Origin header is sent with WebSocket requests, so your server can inspect it and decide whether to accept or reject the connection.

Step-by-Step Guide to Allowing All Origins

Let’s dive into the simple process of allowing all origins (*) in your Gorilla WebSocket server.

  1. Set Up Your WebSocket Server
    You probably have already done this. If not, first make sure you have Gorilla WebSockets installed in your Go project. If you don’t, you can install it with:

    go get github.com/gorilla/websocket
    
  2. Configure the WebSocket Upgrader
    Gorilla WebSocket requires an “upgrader” to convert an HTTP connection to a WebSocket connection. This upgrader needs to be configured to handle the Origin header appropriately.

    Here’s how you can create the upgrader that allows all origins:

    package main
    
    import (
        "fmt"
        "log"
        "net/http"
        "github.com/gorilla/websocket"
    )
    
    var upgrader = websocket.Upgrader{
        CheckOrigin: func(r *http.Request) bool {
            return true
        },
    }
    
    func handler(w http.ResponseWriter, r *http.Request) {
        conn, err := upgrader.Upgrade(w, r, nil)
        if err != nil {
            log.Println(err)
            return
        }
        defer conn.Close()
    
        for {
            _, msg, err := conn.ReadMessage()
            if err != nil {
                log.Println(err)
                return
            }
            fmt.Printf("Received: %s\n", msg)
        }
    }
    
    func main() {
        http.HandleFunc("/", handler)
        log.Fatal(http.ListenAndServe(":8080", nil))
    }
    

    What is going on?

    • The CheckOrigin function inside the upgrader is responsible for validating the origin of incoming WebSocket connections. By setting it to always return true, we are saying, “Hey, any origin is welcome here.”
    • In the handler function, we upgrade the incoming HTTP request to a WebSocket connection using the upgrader.Upgrade method.
  3. Testing It Out
    Now, run your Go server with go run main.go and open your client-side WebSocket connection like this:

    const socket = new WebSocket("ws://localhost:8080");
    socket.onopen = function () {
      console.log("Connected!");
    };
    socket.onmessage = function (event) {
      console.log("Message received:", event.data);
    };
    

    You should see a successful WebSocket connection established, regardless of the origin of your client. This is because the server allows all origins!

Why all the red tape?

While this approach is simple and convenient, allowing all origins (*) isn’t the best practice for production systems. It exposes your WebSocket server to potential security risks, like cross-site WebSocket hijacking . You should limit the origins to trusted domains, especially for production environments, like so:

CheckOrigin: func(r *http.Request) bool {
    origin := r.Header.Get("Origin")
    return origin == "https://trusted.domain.com"
},

This ensures that only requests from https://trusted.domain.com can connect to your WebSocket server.

By setting CheckOrigin to always return true, you can easily allow WebSocket connections from any origin while developing your project. Just keep in mind that once you’re ready to go live, it’s a good idea to lock down the origins to maintain security.

The world is full of choices, and some of them are better than others. This one? It’s a quick shortcut, but it’s on you to decide when to dial things back for security.