Frontend Labs
⋯

39. Terminal project: a rate-limited reverse proxy (mini API gateway)

Goal: combine net/http/httputil.ReverseProxy, the middleware chain from Chapter 24, and rate limiting from Chapter 29 into a small gateway — directly in the spirit of the networking/security field guides' WAF and API-protection material.

package main

import (
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"sync"

	"golang.org/x/time/rate"
)

type ipLimiters struct {
	mu       sync.Mutex
	limiters map[string]*rate.Limiter
}

func newIPLimiters() *ipLimiters {
	return &ipLimiters{limiters: map[string]*rate.Limiter{}}
}

func (l *ipLimiters) get(ip string) *rate.Limiter {
	l.mu.Lock()
	defer l.mu.Unlock()
	lim, ok := l.limiters[ip]
	if !ok {
		lim = rate.NewLimiter(rate.Limit(5), 10) // 5 req/s, burst 10, per IP
		l.limiters[ip] = lim
	}
	return lim
}

func withRateLimit(limiters *ipLimiters, next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ip := clientIP(r)
		if !limiters.get(ip).Allow() {
			http.Error(w, "rate limit exceeded", http.StatusTooManyRequests)
			return
		}
		next.ServeHTTP(w, r)
	})
}

func clientIP(r *http.Request) string {
	if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" {
		return fwd // production: parse + validate against a trusted proxy list
	}
	return r.RemoteAddr
}

func main() {
	target, _ := url.Parse("http://localhost:8080") // your upstream service
	proxy := httputil.NewSingleHostReverseProxy(target)

	limiters := newIPLimiters()
	handler := withRateLimit(limiters, proxy)

	log.Println("gateway listening on :9443, proxying to", target)
	log.Fatal(http.ListenAndServe(":9443", handler))
}

httputil.ReverseProxy rewrites the request's Host/URL, forwards it to target, and streams the response back — the rate limiter middleware runs first and short-circuits with 429 Too Many Requests before the upstream ever sees an over-quota request. Chapter 49 extends this exact shape into a fuller WAF-style middleware with IP blocklists and request inspection.

Applied in the series: the HTTPS guide's reverse proxy chapter covers Rewrite vs Director and X-Forwarded-For trust, and reproduces request smuggling's root cause.


Generated from real-life-golang-guide.md · 8 Oct 2026 16:36