go 52 lines · 7 steps

Building a hardened Go reverse proxy

How to wrap httputil.ReverseProxy with sane timeouts, header rewriting, and error handling for an edge gateway.

Explained by highlit
1package proxy
2 
3import (
4 "log"
5 "net"
6 "net/http"
7 "net/http/httputil"
8 "net/url"
9 "time"
10)
11 
12func NewGatewayProxy(target string) (*httputil.ReverseProxy, error) {
13 backend, err := url.Parse(target)
14 if err != nil {
15 return nil, err
16 }
17 
18 proxy := httputil.NewSingleHostReverseProxy(backend)
19 
20 proxy.Transport = &http.Transport{
21 DialContext: (&net.Dialer{
22 Timeout: 5 * time.Second,
23 KeepAlive: 30 * time.Second,
24 }).DialContext,
25 MaxIdleConns: 100,
26 IdleConnTimeout: 90 * time.Second,
27 TLSHandshakeTimeout: 5 * time.Second,
28 ResponseHeaderTimeout: 10 * time.Second,
29 }
30 
31 baseDirector := proxy.Director
32 proxy.Director = func(req *http.Request) {
33 baseDirector(req)
34 req.Host = backend.Host
35 req.Header.Set("X-Forwarded-Host", req.Header.Get("Host"))
36 req.Header.Set("X-Forwarded-Proto", "https")
37 req.Header.Del("Authorization")
38 }
39 
40 proxy.ModifyResponse = func(resp *http.Response) error {
41 resp.Header.Set("Via", "1.1 edge-gateway")
42 resp.Header.Del("Server")
43 return nil
44 }
45 
46 proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
47 log.Printf("proxy error for %s %s: %v", r.Method, r.URL.Path, err)
48 http.Error(w, "upstream unavailable", http.StatusBadGateway)
49 }
50 
51 return proxy, nil
52}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A production reverse proxy needs an explicit Transport with timeouts so a slow upstream can't exhaust connections.
  2. 2Wrapping the default Director lets you rewrite requests while keeping the library's built-in URL routing intact.
  3. 3ModifyResponse and ErrorHandler give you control over what clients see on both success and upstream failure.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a hardened Go reverse proxy — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code