SSourav Saha
HomeExperienceSoftware DesignSystem DesignLearningBooksToolsContact
SSourav Saha

Building scalable backend systems, distributed infrastructure, and cloud-native applications.

Navigation

  • Home
  • Experience
  • Software Design
  • System Design
  • Learning

More

  • Books
  • Tools
  • Contact

Connect

  • LinkedIn
  • Email

© 2026 Sourav Saha. All rights reserved.

Built with using Next.js

System DesignReverse Proxy (Nginx, Envoy, HAProxy)
System Designintermediate

Reverse Proxy (Nginx, Envoy, HAProxy)

Understand how reverse proxies route requests, terminate TLS, balance traffic, and cache responses. Learn the difference between forward proxies and reverse proxies with Nginx and Envoy examples.

June 5, 202411 min read
reverse-proxynginxenvoyhaproxynetworkingbuilding-blocktier-1

Metadata

FieldValue
Slugreverse-proxy-api-gateway
DifficultyIntermediate
Estimated Reading Time12 min
Estimated Coding Time15 min
Tier1 — Networking & Compute
Implementation LanguageGo
SEO DescriptionLearn Reverse Proxy and API Gateway concepts. Understand how Nginx, Kong, and AWS API Gateway handle routing, SSL termination, and rate limiting. Go implementation.

1. Overview

What problem does it solve?

When a user opens https://myshop.com/products, their browser has no idea that behind the scenes, there are 50 different microservices handling different parts of the request (Products, Auth, Payments, etc.).

A Reverse Proxy sits between the internet and your internal services. It accepts all incoming traffic, inspects the request (URL, headers), and routes it to the correct internal service. The client never knows the internal architecture.

An API Gateway is a specialized Reverse Proxy that adds business-level features on top: Authentication, Rate Limiting, Request Transformation, and Analytics.

What breaks without it?

  • Exposed Infrastructure: Without a reverse proxy, clients must know the IP address of every single microservice. If you change an IP, every client breaks.
  • No Central Security: Each microservice must individually handle SSL certificates, rate limiting, and authentication. This is duplicated effort and a security nightmare.

2. Motivation

Forward Proxy vs Reverse Proxy

FeatureForward Proxy (e.g., VPN)Reverse Proxy (e.g., Nginx)
Who it protectsThe ClientThe Server
Who initiatesThe Client (browses internet via proxy)The Client (hits proxy, gets routed to server)
Use CaseCorporate VPNs, bypassing geo-restrictionsLoad balancing, SSL termination, caching

A Forward Proxy hides the client's identity from the server. A Reverse Proxy hides the server's identity from the client.

Nginx started as a web server in 2004 and quickly became the dominant reverse proxy due to its event-driven, non-blocking architecture. As microservices grew, companies like Kong and AWS built full-featured API Gateways on top of Nginx (or custom proxies) to handle authentication, request transformation, and developer portals.


3. Real-World Usage

SystemUse Case
NginxThe most popular open-source reverse proxy. Handles SSL termination, static file serving, and load balancing for millions of websites.
KongAn open-source API Gateway built on Nginx (OpenResty). Adds plugins for auth, rate limiting, and logging.
AWS API GatewayA managed serverless API Gateway for routing requests to Lambda functions or EC2 instances.
CloudflareActs as a global reverse proxy (CDN) that also provides DDoS protection and WAF (Web Application Firewall).

4. Prerequisites

ConceptBlock
HTTP Fundamentals001 HTTP & TCP Fundamentals
Load Balancing003 Load Balancer

5. Visual Explanation

The API Gateway Architecture

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#ffcc00', 'edgeLabelBackground':'#ffffff'}}}%%
graph TD
    classDef client fill:#f9f9f9,stroke:#333,stroke-width:2px;
    classDef gw fill:#d4edda,stroke:#28a745,stroke-width:3px;
    classDef svc fill:#cce5ff,stroke:#007bff,stroke-width:2px;

    C((Client<br/>Browser)):::client --> |"HTTPS"| GW

    subgraph API Gateway
        GW{{"Nginx / Kong<br/>──────────────<br/>1. SSL Termination<br/>2. Auth Check<br/>3. Rate Limiting<br/>4. Route by Path"}}:::gw
    end
    
    GW --> |"/api/products"| S1[Product Service<br/>:3001]:::svc
    GW --> |"/api/users"| S2[User Service<br/>:3002]:::svc
    GW --> |"/api/payments"| S3[Payment Service<br/>:3003]:::svc

6. Internal Working

Key Responsibilities

  1. SSL/TLS Termination: The gateway holds the single SSL certificate for myshop.com. It decrypts the incoming HTTPS request and forwards plain HTTP to the internal services (which run on a trusted internal network). This eliminates the need for every microservice to manage its own certificate.

  2. Path-Based Routing:

    • /api/v1/products/* → Route to product-service:3001
    • /api/v1/users/* → Route to user-service:3002
  3. Authentication & Authorization: The gateway validates JWT tokens or API keys before the request reaches any backend service. If the token is invalid, the gateway immediately returns 401 Unauthorized.

  4. Rate Limiting: The gateway enforces rate limits (e.g., 100 requests per minute per API key) centrally, protecting all backend services from abuse.

  5. Request/Response Transformation: The gateway can modify headers (e.g., add X-Request-Id), strip sensitive headers, aggregate responses from multiple services (BFF - Backend For Frontend), or convert protocols (REST to gRPC).


7. Implementation

Why Go? Go's net/http/httputil.ReverseProxy is a production-grade reverse proxy implementation used in real tools like Traefik. We can build a fully functional API gateway in under 100 lines.

/*
036 - Reverse Proxy & API Gateway
A Go implementation of a simple API Gateway that performs:
  - Path-based routing to multiple backend services
  - JWT Authentication (mock)
  - Request ID injection
  - Logging
*/
package main

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

// ── 1. Mock Backend Services ──

func startProductService() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		reqID := r.Header.Get("X-Request-Id")
		fmt.Fprintf(w, `{"service": "ProductService", "path": "%s", "request_id": "%s"}`, r.URL.Path, reqID)
	})
	http.ListenAndServe(":3001", mux)
}

func startUserService() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		reqID := r.Header.Get("X-Request-Id")
		fmt.Fprintf(w, `{"service": "UserService", "path": "%s", "request_id": "%s"}`, r.URL.Path, reqID)
	})
	http.ListenAndServe(":3002", mux)
}

// ── 2. The API Gateway ──

type APIGateway struct {
	routes map[string]*httputil.ReverseProxy
}

func NewAPIGateway() *APIGateway {
	gw := &APIGateway{routes: make(map[string]*httputil.ReverseProxy)}

	// Define routes: Path Prefix -> Backend Service URL
	gw.addRoute("/api/products", "http://localhost:3001")
	gw.addRoute("/api/users", "http://localhost:3002")

	return gw
}

func (gw *APIGateway) addRoute(pathPrefix, target string) {
	targetURL, _ := url.Parse(target)
	proxy := httputil.NewSingleHostReverseProxy(targetURL)
	gw.routes[pathPrefix] = proxy
}

func (gw *APIGateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	start := time.Now()

	// --- Middleware 1: Authentication ---
	authHeader := r.Header.Get("Authorization")
	if authHeader != "Bearer valid-token" {
		log.Printf("[Gateway] ❌ Auth Failed for %s %s", r.Method, r.URL.Path)
		http.Error(w, `{"error": "Unauthorized"}`, http.StatusUnauthorized)
		return
	}

	// --- Middleware 2: Inject Request ID ---
	requestID := fmt.Sprintf("req-%d", time.Now().UnixNano())
	r.Header.Set("X-Request-Id", requestID)

	// --- Middleware 3: Path-based Routing ---
	for prefix, proxy := range gw.routes {
		if strings.HasPrefix(r.URL.Path, prefix) {
			// Strip the gateway prefix from the path before forwarding
			r.URL.Path = strings.TrimPrefix(r.URL.Path, prefix)
			if r.URL.Path == "" {
				r.URL.Path = "/"
			}

			log.Printf("[Gateway] ✅ Routing %s -> %s (ReqID: %s, Latency: %v)",
				r.URL.Path, prefix, requestID[:15], time.Since(start))
			
			proxy.ServeHTTP(w, r)
			return
		}
	}

	// --- No route matched ---
	http.Error(w, `{"error": "Not Found"}`, http.StatusNotFound)
}

// ── 3. Test Harness ──

func main() {
	// Start backend services
	go startProductService()
	go startUserService()
	time.Sleep(100 * time.Millisecond)

	// Start the API Gateway
	gw := NewAPIGateway()
	
	fmt.Println("🚀 API Gateway running on :8080")
	fmt.Println("   Routes:")
	fmt.Println("     /api/products -> :3001")
	fmt.Println("     /api/users    -> :3002")
	fmt.Println("")
	
	// Simulate requests
	// --- Request 1: Valid Auth, Product route ---
	client := &http.Client{}
	req, _ := http.NewRequest("GET", "http://localhost:8080/api/products/iphone", nil)
	req.Header.Set("Authorization", "Bearer valid-token")
	resp, _ := client.Do(req)
	if resp != nil {
		buf := make([]byte, 512)
		n, _ := resp.Body.Read(buf)
		resp.Body.Close()
		fmt.Printf("Response 1: %s\n", string(buf[:n]))
	}

	// --- Request 2: Invalid Auth ---
	req2, _ := http.NewRequest("GET", "http://localhost:8080/api/users/me", nil)
	req2.Header.Set("Authorization", "Bearer bad-token")
	resp2, _ := client.Do(req2)
	if resp2 != nil {
		buf := make([]byte, 512)
		n, _ := resp2.Body.Read(buf)
		resp2.Body.Close()
		fmt.Printf("Response 2: %s (Status: %d)\n", string(buf[:n]), resp2.StatusCode)
	}
}

Sample Output

🚀 API Gateway running on :8080
   Routes:
     /api/products -> :3001
     /api/users    -> :3002

[Gateway] ✅ Routing /iphone -> /api/products (ReqID: req-171294567, Latency: 42µs)
Response 1: {"service": "ProductService", "path": "/iphone", "request_id": "req-1712945678901"}

[Gateway] ❌ Auth Failed for GET /api/users/me
Response 2: {"error": "Unauthorized"} (Status: 401)

8. Complexity

MetricDetails
Latency Overhead1-5ms for a typical Nginx reverse proxy hop.
ThroughputNginx can handle ~100,000+ concurrent connections on a single server due to its non-blocking, event-driven architecture.

9. Trade-offs

SetupProsCons
Direct Client-to-ServiceZero latency overhead. Simple.Exposes internal services. No centralized auth or rate limiting.
Reverse Proxy (Nginx)SSL termination. Load balancing. Caching.Adds a network hop. Becomes a single point of failure if not deployed redundantly.
API Gateway (Kong / AWS APIGW)Full-featured: Auth, Rate Limiting, Analytics, Developer Portal.More complex to operate. Can become a performance bottleneck if not scaled horizontally. Higher cost (SaaS).

10. Production Evolution

FeatureThis ImplementationProduction (Kong / AWS APIGW)
AuthHardcoded string checkOAuth 2.0, OpenID Connect, JWT validation via JWKS endpoint, API Key management.
Rate LimitingNoneConfigurable per-consumer, per-route, per-IP rate limits backed by Redis.
VersioningNoneRoute /api/v1/* to old service, /api/v2/* to new service. Enable canary deployments.

11. Common Bugs

BugWhat happensFix
Single Point of FailureYou deploy one Nginx instance. It crashes. Your entire website goes offline.Deploy at least 2 Nginx instances behind a cloud load balancer (e.g., AWS ALB) for high availability.
Large Request BodyA user uploads a 500MB video. Nginx buffers the entire file in memory before forwarding it, crashing the gateway with an OOM.Configure proxy_request_buffering off; and client_max_body_size 1g; in Nginx.
Stale DNS CacheThe IP of product-service changes (e.g., Kubernetes rolls out a new pod). Nginx resolved the DNS at startup and has the old IP forever.In Nginx, use resolver and set upstream to force DNS re-resolution on every request.

12. Interview Questions

  1. What is the difference between a Reverse Proxy and a Load Balancer? Hint: A Load Balancer distributes traffic across multiple instances of the SAME service. A Reverse Proxy routes traffic to DIFFERENT services based on the request path/headers. In practice, Nginx does both.

  2. What is SSL Termination? Hint: The reverse proxy decrypts the HTTPS connection from the client. It then forwards the request as plain HTTP to the internal backend services. This centralizes certificate management to one place.

  3. What is the difference between an API Gateway and a Service Mesh? Hint: An API Gateway sits at the edge (North-South traffic: Client to Server). A Service Mesh handles internal communication (East-West traffic: Server to Server). They are complementary.


13. Used By (Downstream Blocks)

  • 003 Load Balancer — Often combined with or deployed behind a reverse proxy.
  • 035 Service Mesh — Complementary. The gateway handles external traffic; the mesh handles internal traffic.

14. Used In (Case Studies)

SystemUse Case
Netflix (Zuul / Spring Cloud Gateway)All external Netflix traffic flows through Zuul, which handles authentication, routing, and canary releases.
StripeUses API Gateways to manage API key auth, versioning, and rate limiting for millions of API consumers.
Any Public APIEvery major public API (Twitter, GitHub, Google Maps) uses an API Gateway to manage access.

15. Related Blocks

RelationshipBlock
Previous003 Load Balancer
Parallel035 Service Mesh

16. Try It Yourself

Exercise 1: Rate Limiting Middleware

Add a simple rate limiter to the ServeHTTP method. Use a Go map[string]int to track the number of requests per API key (from the Authorization header) in the last 60 seconds. If the count exceeds 10, return 429 Too Many Requests.

Exercise 2: Response Aggregation (BFF)

Create a new route /api/dashboard that doesn't forward to a single backend. Instead, it simultaneously calls both /api/products and /api/users using goroutines, waits for both responses, merges the two JSON objects into one, and returns the combined result to the client.


Website Metadata

FieldValue
Hero TitleReverse Proxy & API Gateway
Hero SubtitleThe front door of the internet — how Nginx and Kong route, protect, and transform every request before it reaches your services.
BreadcrumbSystem Design → Building Blocks → API Gateway
Sidebar CategoryTier 1 — Networking
Search Keywordsreverse proxy, api gateway, nginx, kong, ssl termination, routing, rate limiting, load balancing
Internal Links← 003 Load Balancer · ← 035 Service Mesh
Suggested IllustrationA grand hotel lobby with a concierge (API Gateway) standing at the front desk. Guests (requests) arrive and the concierge checks their reservation (Auth), directs them to the correct floor (Routing), and refuses entry to uninvited guests.
Suggested AnimationA client request arrives at a gate. The gate checks an ID badge (Auth), stamps it with a tracking number (Request ID), and opens one of three doors (Routing) to the correct backend service.
PreviousGossip ProtocolNextService Mesh