Writing a reverse proxy in Go is an absolute delight thanks to the httputil package’s NewSingleHostReverseProxy
function.
package main
import (
"crypto/tls"
"log"
"net/http"
"net/http/httputil"
"net/url"
"time"
)
var (
targetUrl *url.URL
)
func main() {
var err error
targetUrl, err = url.Parse("https://url-to-direct/")
if err != nil {
panic(err)
}
handler := http.NewServeMux()
handler.HandleFunc("/", proxyHandler)
server := &http.Server{
Addr: ":8080",
Handler: handler,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 15 * time.Second,
}
server.SetKeepAlivesEnabled(true)
log.Printf("Listening on %s\n", server.Addr)
log.Fatal(server.ListenAndServe())
}
func proxyHandler(writer http.ResponseWriter, request *http.Request) {
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
httputil.NewSingleHostReverseProxy(targetUrl).ServeHTTP(writer, request)
}
Note that if there’s a reverse proxy (i.e. Nginx, Traefik, Ambassador, etc.) on the receiving end, you will most likely also need to override the request host to the target host:
func proxyHandler(writer http.ResponseWriter, request *http.Request) {
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
request.Host = targetUrl.Host
httputil.NewSingleHostReverseProxy(targetUrl).ServeHTTP(writer, request)
}
If you want to expose the proxied service through a specific path on your proxy rather than exposing it on /
,
then you could do something like this:
handler.HandleFunc("/external-service/", externalServiceHandler)
// ...
func externalServiceHandler(writer http.ResponseWriter, request *http.Request) {
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
request.Host = targetUrl.Host
request.URL.Path = strings.TrimPrefix(request.URL.Path, "/external-service")
httputil.NewSingleHostReverseProxy(targetUrl).ServeHTTP(writer, request)
}
Keep in mind that while this shouldn’t cause any problems if you’re using a proxy to reach an API, it’s very likely that you’ll run into weird issues if you try to proxy to a front-end application due to absolute and relative paths.