Search code examples
gocorsreverse-proxy

multiple CORS values being rejected in Go gin api


In my Go API, I'm using gin, and I have one value set in my Access-Control-Allow-Origin header. If I have more than one, my react UI throws an error to the effect of The Access-Control-Allow-Origin header contains multiple values 'http://value1, http://value2', but only one is allowed.... I need to set multiple values. How do I do this?

The API is a reverse proxy, and here's the relevant code:

func proxy(c *gin.Context) {
  var remote = "myUrl"
  proxy := httputil.NewSingleHostReverseProxy(remote)
  proxy.Director = func(req *http.Request) {
        req.Header.Set("Authorization", "My Auth Values")
        req.Host = remote.Host
        req.URL.Scheme = remote.Scheme
        req.URL.Host = remote.Host
    }
    proxy.ModifyResponse = addCustomHeader
    proxy.ServeHTTP(c.Writer, c.Request)
}

func addCustomHeader(r *http.Response) error {
    r.Header["Access-Control-Allow-Origin"] = []string{"value1"}
    return nil
}

Solution

  • A CORS header can only contain a single value. If you want to implement your own CORS middleware, you need to work around that fact.

    A simple CORS middleware will add the Access-Control-Allow-Origin header with the value of the specific address of the incoming request, usually taken from the Referer or Origin header. Typically, you match this against a list or map first, to see if it's in your allow list. If so, then the address of the request is added as allowed origin (as a single value).

    A simple example could look like this

    allowList := map[string]bool{
        "https://www.google.com": true,
        "https://www.yahoo.com":  true,
    }
    
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if origin := r.Header.Get("Origin"); allowList[origin] {
            w.Header().Set("Access-Control-Allow-Origin", origin)
        }
    })
    

    Since you are using the reverse proxy, you can access the request from the response.

    mod := func(allowList map[string]bool) func(r *http.Response) error {
        return func(r *http.Response) error {
            if origin := r.Request.Header.Get("Origin"); allowList[origin] {
                r.Header.Set("Access-Control-Allow-Origin", origin)
            }
            return nil
        }
    }
    
    proxy := &httputil.ReverseProxy{
        Director: func(r *http.Request) {
            r.URL.Scheme = "https"
            r.URL.Host = "go.dev"
            r.Host = r.URL.Host
        },
        ModifyResponse: mod(allowList),
    }