I faced an unusal case at work where I needed to write a reverse proxy that would inject a specific header read from a file to handle the request. While I do not write professionally in Go, I thought it would be good enough for the task at hand. I did not know where to start and googling did show some other efforts, so I asked at Golangs’s Slack. When in doubt always ask. Hopefully someone will pick it up and answer. I was pointed to NewSingleHostReverseProxy. So the simplest reverse proxy in Go can look like this:
package main
import (
"log"
"net/http"
"net/http/httputil"
"net/url"
)
func main() {
target, _ := url.Parse("https://api.chucknorris.io")
proxy := httputil.NewSingleHostReverseProxy(target)
http.Handle("/", proxy)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal(err)
}
}
This is the simplest reverse proxy and if you call it with curl -H 'Host: api.chucknorris.io' http://localhost:8080/jokes/random you are going to get back a joke.
But say we want to add a header, what do we do? We can define a Director function in our program that will allow us to do so:
proxy := httputil.NewSingleHostReverseProxy(target)
proxy.Director = func(req *http.Request) {
req.Header.Set("X-Foo", "Bar")
}
Now trying the same curl command we get the error 2024/11/17 19:41:53 http: proxy error: unsupported protocol scheme "". That is because by defining the Director function, we have lost the previous one and we have to rebuild the scheme (and a couple of other headers after). There is though a better way to do this, by keeping track of the previous default Director:
proxy := httputil.NewSingleHostReverseProxy(target)
d := proxy.Director
proxy.Director = func(req *http.Request) {
d(req)
req.Header.Set("X-Foo", "Bar")
}
And now you are set. I am certain there are better and more performant ways to do this, but for the PoC I was working on, that was good enough.
[*] I used api.chucknorris.io for the blog post because it is one of the simplest open APIs out there to try stuff.
[**] The complete reverse proxy, taking care of error handling and environment variables is 65 lines.