main.go
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
package main

import (
	"crypto/subtle"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"
)

func main() {
	target, err := url.Parse("http://127.0.0.1:8081")
	if err != nil {
		log.Fatalf("parse target: %v", err)
	}

	proxy := httputil.NewSingleHostReverseProxy(target)
	proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
		http.Error(w, fmt.Sprintf("upstream error: %v", err), http.StatusBadGateway)
	}
	proxy.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   5 * time.Second,
			KeepAlive: 30 * time.Second,
		}).DialContext,
		MaxIdleConns:          100,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   5 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
	}

	const expectedToken = "dev-secret"

	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := r.Header.Get("Authorization")
		want := "Bearer " + expectedToken
		if subtle.ConstantTimeCompare([]byte(token), []byte(want)) != 1 {
			http.Error(w, "unauthorized", http.StatusUnauthorized)
			return
		}

		r.Header.Del("Connection")
		r.Header.Set("X-Forwarded-Host", r.Host)
		proxy.ServeHTTP(w, r)
	})

	srv := &http.Server{
		Addr:              ":8080",
		Handler:           handler,
		ReadHeaderTimeout: 5 * time.Second,
	}

	log.Printf("proxy listening on http://localhost%v -> %s", srv.Addr, target.String())
	if err := srv.ListenAndServe(); err != nil {
		log.Fatalf("proxy failed: %v", err)
	}
}

How It Works

Reverse proxy built on net/http/httputil that forwards requests, adds simple bearer authentication, and enforces timeouts.

Configures an HTTP transport with dial and response timeouts, wraps ReverseProxy director to inject authentication and header logic, handles errors via a custom error handler, and starts a server exposing the proxy endpoint.

Key Concepts

  • 1Custom director rewrites target host or scheme and headers.
  • 2Bearer-token check rejects unauthorized requests early.
  • 3Transport timeouts and error handling guard against hanging upstreams.

When to Use This Pattern

  • Gateway for internal services with lightweight authentication.
  • Local development proxying to remote APIs securely.
  • Adding cross-cutting concerns such as headers or logging in one place.

Best Practices

  • Do not log sensitive authorization headers.
  • Set sane idle and response timeouts to avoid resource exhaustion.
  • Validate upstream URLs and restrict where requests can be sent.
Go Version1.18+
Difficultyadvanced
Production ReadyYes
Lines of Code61