Metadata
| Field | Value |
|---|---|
| Slug | reverse-proxy-api-gateway |
| Difficulty | Intermediate |
| Estimated Reading Time | 12 min |
| Estimated Coding Time | 15 min |
| Tier | 1 — Networking & Compute |
| Implementation Language | Go |
| SEO Description | Learn 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
| Feature | Forward Proxy (e.g., VPN) | Reverse Proxy (e.g., Nginx) |
|---|---|---|
| Who it protects | The Client | The Server |
| Who initiates | The Client (browses internet via proxy) | The Client (hits proxy, gets routed to server) |
| Use Case | Corporate VPNs, bypassing geo-restrictions | Load 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
| System | Use Case |
|---|---|
| Nginx | The most popular open-source reverse proxy. Handles SSL termination, static file serving, and load balancing for millions of websites. |
| Kong | An open-source API Gateway built on Nginx (OpenResty). Adds plugins for auth, rate limiting, and logging. |
| AWS API Gateway | A managed serverless API Gateway for routing requests to Lambda functions or EC2 instances. |
| Cloudflare | Acts as a global reverse proxy (CDN) that also provides DDoS protection and WAF (Web Application Firewall). |
4. Prerequisites
| Concept | Block |
|---|---|
| HTTP Fundamentals | 001 HTTP & TCP Fundamentals |
| Load Balancing | 003 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
-
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. -
Path-Based Routing:
/api/v1/products/*→ Route toproduct-service:3001/api/v1/users/*→ Route touser-service:3002
-
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. -
Rate Limiting: The gateway enforces rate limits (e.g., 100 requests per minute per API key) centrally, protecting all backend services from abuse.
-
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
| Metric | Details |
|---|---|
| Latency Overhead | 1-5ms for a typical Nginx reverse proxy hop. |
| Throughput | Nginx can handle ~100,000+ concurrent connections on a single server due to its non-blocking, event-driven architecture. |
9. Trade-offs
| Setup | Pros | Cons |
|---|---|---|
| Direct Client-to-Service | Zero 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
| Feature | This Implementation | Production (Kong / AWS APIGW) |
|---|---|---|
| Auth | Hardcoded string check | OAuth 2.0, OpenID Connect, JWT validation via JWKS endpoint, API Key management. |
| Rate Limiting | None | Configurable per-consumer, per-route, per-IP rate limits backed by Redis. |
| Versioning | None | Route /api/v1/* to old service, /api/v2/* to new service. Enable canary deployments. |
11. Common Bugs
| Bug | What happens | Fix |
|---|---|---|
| Single Point of Failure | You 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 Body | A 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 Cache | The 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
-
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.
-
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.
-
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)
| System | Use Case |
|---|---|
| Netflix (Zuul / Spring Cloud Gateway) | All external Netflix traffic flows through Zuul, which handles authentication, routing, and canary releases. |
| Stripe | Uses API Gateways to manage API key auth, versioning, and rate limiting for millions of API consumers. |
| Any Public API | Every major public API (Twitter, GitHub, Google Maps) uses an API Gateway to manage access. |
15. Related Blocks
| Relationship | Block |
|---|---|
| Previous | 003 Load Balancer |
| Parallel | 035 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
| Field | Value |
|---|---|
| Hero Title | Reverse Proxy & API Gateway |
| Hero Subtitle | The front door of the internet — how Nginx and Kong route, protect, and transform every request before it reaches your services. |
| Breadcrumb | System Design → Building Blocks → API Gateway |
| Sidebar Category | Tier 1 — Networking |
| Search Keywords | reverse proxy, api gateway, nginx, kong, ssl termination, routing, rate limiting, load balancing |
| Internal Links | ← 003 Load Balancer · ← 035 Service Mesh |
| Suggested Illustration | A 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 Animation | A 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. |