Writing your own Reverse Proxy server using Golang
Writing a Reverse Proxy server in Go is a matter of single digit lines of code due to its rock solid standard library and its low level network plumbing capabilities. Recently I came across some use cases where I needed to write my own Reverse Proxy server and of course Go was the pragmatic choice.

Let us first start with defining Reverse Proxy and its common uses.
Reverse Proxy
A Reverse Proxy is an intermediary server that sits between multiple clients and servers, and directs client requests to appropriate backend server. It is commonly used for:
- 
Load Balancing – It can distribute the load across multiple available backend servers in such manner that maximizes speed and capacity utilization while ensuring no one server is overloaded, which can degrade performance.
 - 
Security – By intercepting requests headed for your backend servers, a reverse proxy server protects their identities and acts as an additional defense against security attacks.
 - 
Caching – It can cache content to serve, and you can deploy regional proxies to cache content related to specific regions.
 - 
TLS Termination – We can use the reverse proxy to terminate the SSL/TLS connection with the clients in cases where backend servers are being hosted on the same secure VPC network, and it’s ok to let the
reverse proxy<->backend serverconnection insecure. - 
More – Literally, there are a lot more use cases that can take leverage of reverse proxies, that I leave for you to explore.
 
Personal Use Cases
Some common use cases I personally use reverse proxy server for are:
- 
TLS Termination – Lets say I have multiple servers running on the same VPC network, each has it’s own copy of the
TLS certificateissued to same domain name, it makes sense to let thereverse proxyhandle the TLS handshake and terminate the secure connection and forward it insecurely to appropriate backend server on the same VPC isolated from public reach, now instead of havingTLS certson every backend server you can have them on proxy only. - 
Single VPS Dev Environment – I have multiple backend servers for a single project, each with separate responsibilities, that I need to deploy but the servers would sit ideal 99% of the time I don’t want to spin up multiple VPS for each for a dev environment, which would cost me money, also I don’t want to explicitly mention the
port numberson the client side to have the servers run on the same VPS. To solve this, I can run all the backend servers andreverse proxyon a single VPS, and have the requests proxied to servers running on different ports. 
Also sometimes for Authorization, Caching or implementing a feature like Request Limit per client.
Let’s Code
Enough with the uses, lets jump over to implementation. You are probably already familiar with Go standard library’s net/http package, there’s also a utility package for it you might have or might not have used, that is net/http/httputil. We’ll be using this utility package because it has the type ReverseProxy defined which makes writing a reverse proxy so simple using Go.
Conditions
Let’s define some condition based on which we’ll be forwarding the requests to appropriate server. For this tutorial lets stick to those conditions being the domain name or host of the requests:
- If request came on host 
sub1.xyz.com, forward tolocalhost:8080. - If request came on host 
sub2.xyz.com, forward tolocalhost:8181. - If request came from an unknown host, forward to 
localhost:8888. 
main.go
Now lets create a main.go file, which will do the following:
 1    package main
 2
 3    import "net/http"
 4
 5    func main() {
 6        http.HandleFunc("/", proxy)
 7        if err := http.ListenAndServe(":4433", nil); err != nil {
 8            panic(err)
 9        }
10    }
11
12    func proxy(w http.ResponseWriter, r *http.Request) {
13        // We will get to this later ...
14    }
Now that we have a basic server in place, let’s write our conditions for reverse proxy and use it to forward requests inside our proxy handler.
 1    package main
 2
 3    import (
 4        "net/http"
 5        "net/http/httputil"
 6        "net/url"
 7        "strings"
 8    )
 9
10    func main() {
11        http.HandleFunc("/", proxy)
12        if err := http.ListenAndServe(":4433", nil); err != nil {
13            panic(err)
14        }
15    }
16
17    func proxy(w http.ResponseWriter, r *http.Request) {
18        if strings.HasSuffix(r.Host, "://sub1.xyz.com") { // forward requests from sub1.xyz.com to localhost:8080
19            if url, err := url.Parse("http://localhost:8080"); err == nil {
20                proxy := httputil.NewSingleHostReverseProxy(url)
21                proxy.ServeHTTP(w, r)
22            }
23        } else if strings.HasSuffix(r.Host, "://sub2.xyz.com") { // forward requests from sub2.xyz.com to localhost:8181
24            if url, err := url.Parse("http://localhost:8181"); err == nil {
25                proxy := httputil.NewSingleHostReverseProxy(url)
26                proxy.ServeHTTP(w, r)
27            }
28        } else { // forward requests from unknown host to localhost:8888
29            if url, err := url.Parse("http://localhost:8888"); err == nil {
30                proxy := httputil.NewSingleHostReverseProxy(url)
31                proxy.ServeHTTP(w, r)
32            }
33        }
34    }
The httputil’s NewSingleHostReverseProxy(url) function returns a new ReverseProxy that routes requests to url provided. And that’s all, or what minimal we need to run a reverse proxy based on conditions we defined.
But there are many ways we can refactor this code, starting from using predefined proxy objects instead of creating a new one on each request. Or I prefer a map map[string]*httputil.ReverseProxy which maps hosts to proxies, so we can use the map to check if a proxy exists for host then use it, else use the default one. I’ll leave the refactoring to anyone following this tutorial to keep it short.
Next Steps
Though it is pretty simple to write a reverse proxy using Go’s standard library, but it’s missing a lot of good stuff that comes with nginx or other solutions available. So you can start by implementing some of them into your own reverse proxy that is if the available solutions aren’t feasible and you are having to develop your own, like:
- Caching
 - Logging
 - Protocol Support (HTTP, HTTPS, HTTP/1.1, HTTP/2, …)
 - Rate Limit
 - TLS Support
 - Load Balancing
 - …