Hero image for Proxy Servers Demystified: Forward, Reverse & Everything In Between

Proxy Servers Demystified: Forward, Reverse & Everything In Between

networking proxy Tunneling infrastructure devops security FaaS

Every request you make on the internet passes through at least one intermediary. Understanding what those intermediaries do — and how to configure them — is one of the most practical networking skills you can build.


1. What Is a Proxy Server?

A proxy server is a middleman that sits between a client and a destination server. Instead of talking directly, both sides talk through the proxy.

┌──────────┐         ┌───────────┐         ┌──────────────┐
│  Client  │────────▶│   Proxy   │────────▶│  Destination │
│ (You)    │◀────────│  Server   │◀────────│    Server    │
└──────────┘         └───────────┘         └──────────────┘
     request ──▶          ──▶           ──▶
     response ◀──         ◀──           ◀──

Why do proxies exist?

GoalHow a Proxy Helps
PrivacyHides the client’s real IP address
SecurityFilters malicious traffic, terminates SSL
PerformanceCaches responses, compresses data
Access ControlBlocks or allows specific URLs/domains
Dev WorkflowTunnels local servers to the public internet

The key question that determines the type of proxy is: who does the proxy represent?


2. Forward Proxy vs Reverse Proxy

2.1 Forward Proxy — Represents the Client

The client knows it is using a proxy and explicitly configures it. The destination server only sees the proxy’s IP.

Analogy: A VPN — you route your traffic through a server so websites see the VPN’s IP, not yours.

2.2 Reverse Proxy — Represents the Server

The client has no idea a proxy exists. It thinks it is talking directly to the server. The proxy intercepts the request and forwards it to one or more backend servers.

Analogy: A hotel reception desk — guests (clients) talk to the receptionist, who routes requests to the right department behind the scenes.

2.3 Transparent Proxy — User-Unaware, Network-Level

Neither the client nor the server explicitly configures the proxy. It is injected at the network layer (router, ISP, firewall). The client does not know it exists.

Use cases: ISP caching, corporate monitoring, parental controls.

Forward Proxy                 Reverse Proxy                Transparent Proxy
──────────────                ─────────────                ─────────────────

 Client ──▶ [FWD PROXY] ──▶ Server    Client ──▶ [REV PROXY] ──▶ Backend A    Client ──▶ (network) ──▶ Server
   │                                      │            │──▶ Backend B              │
   │  Client configures                   │            │──▶ Backend C              │  Neither side
   │  the proxy                           │  Server deploys                        │  configures it
   │                                      │  the proxy                             │
   ▼                                      ▼                                        ▼
 Server sees PROXY IP              Client sees PROXY IP                   Proxy is invisible
 Client knows about proxy          Client does NOT know                   Injected by network

Quick Memory Trick

“Who configured the proxy?”

  • Client configured it → Forward Proxy
  • Server configured it → Reverse Proxy
  • Network configured it → Transparent Proxy

3. Forward Proxy Use Cases

A. Privacy & Anonymity (VPN)

A VPN is essentially a forward proxy with encryption. Your traffic exits from the VPN server’s IP, hiding your real location.

Your PC ──encrypted──▶ VPN Server ──▶ Internet
                       (exit IP)

B. Geo-Blocking Bypass

Streaming services restrict content by region. A forward proxy in the target country makes you appear local.

You (Taiwan) ──▶ Proxy (US) ──▶ Netflix US library

C. Content Filtering (Corporate / School Networks)

Organizations deploy forward proxies to block social media, gambling sites, or malware domains during work hours.

Employee ──▶ Corporate Proxy ──▶ Allowed? ──▶ Internet

                                    └── Blocked → 403 Forbidden

D. Dev Debugging (Charles, Fiddler)

Tools like Charles Proxy and Fiddler act as local forward proxies. They intercept HTTP/HTTPS traffic so you can inspect headers, payloads, and timing.

Common workflow:

  1. Set your browser/device to use localhost:8888 as proxy
  2. Install the tool’s root CA certificate (for HTTPS inspection)
  3. Inspect every request/response in real time

How HTTPS Inspection Actually Works (MITM)

Ever wonder how Charles or your company’s firewall can read HTTPS traffic? They perform a Man-In-The-Middle (MITM) attack — but a sanctioned one.

Normal HTTPS:
  Browser ──TLS──▶ google.com   (only Google's cert, nobody can read)

HTTPS Inspection (MITM):
  Browser ──TLS──▶ [Proxy] ──TLS──▶ google.com
              │         │
              │         └── Proxy decrypts, inspects, re-encrypts

              └── Browser trusts proxy's Root CA (installed manually)

How it works step by step:

  1. The proxy intercepts the TLS handshake to google.com
  2. The proxy creates a fake certificate for google.com, signed by its own Root CA
  3. The browser accepts it because you installed the proxy’s Root CA as trusted
  4. The proxy opens a separate real TLS connection to google.com
  5. Traffic flows: Browser → (decrypted at proxy) → re-encrypted → Google

This is why your company can monitor your HTTPS traffic — their corporate proxy’s Root CA is pre-installed on your work laptop. Without that Root CA, the browser would show a certificate warning.

Security implication: Never install Root CAs from untrusted sources. Doing so gives that entity the ability to intercept all your HTTPS traffic.

E. Web Scraping with Proxy Pools

Scraping at scale requires rotating IPs to avoid rate limits and bans. Proxy pool services provide thousands of residential or datacenter IPs.

Scraper ──▶ Proxy Pool (round-robin) ──▶ Target Site

               ├── IP 1 (US)
               ├── IP 2 (DE)
               ├── IP 3 (JP)
               └── IP 4 (BR)

4. Reverse Proxy Use Cases

A. Load Balancing

L4 vs L7 — Where Does the Proxy Operate?

Before diving into algorithms, it helps to understand at which layer the proxy makes routing decisions.

Layer 4 (Transport)Layer 7 (Application)
SeesTCP/UDP packets (IP + port)HTTP headers, URL path, cookies
Routing logicForward packets based on IP:portRoute based on URL, Host header, content type
SpeedFaster (no payload parsing)Slower (must parse HTTP)
Use caseRaw TCP load balancing, database proxyingPath-based routing, A/B testing, canary
ExamplesAWS NLB, HAProxy (TCP mode)Nginx, Envoy, AWS ALB, HAProxy (HTTP mode)
L4 Proxy:  Sees "packet to port 443" → forwards to Backend A
           (doesn't know if it's HTTP, WebSocket, or gRPC)

L7 Proxy:  Sees "GET /api/users HTTP/2" → routes to User Service
           (can inspect headers, rewrite paths, check auth)

In practice, many architectures stack both: an L4 load balancer (e.g., AWS NLB) in front of an L7 reverse proxy (e.g., Nginx or Envoy) for maximum performance and flexibility.

A reverse proxy distributes incoming requests across multiple backend servers.

                         ┌──────────────┐
                    ┌───▶│  Backend #1  │
┌────────┐         │    └──────────────┘
│ Client │──▶ [LB] │    ┌──────────────┐
└────────┘         ├───▶│  Backend #2  │
                   │    └──────────────┘
                   │    ┌──────────────┐
                   └───▶│  Backend #3  │
                        └──────────────┘

Common algorithms:

AlgorithmHow It WorksBest For
Round RobinRotates sequentially: 1 → 2 → 3 → 1Equal-capacity servers
Least ConnectionsSends to the server with fewest active connectionsVariable request duration
IP HashSame client IP always hits the same backendSession stickiness
WeightedAssigns more traffic to stronger serversMixed-capacity fleet

B. SSL/TLS Termination

The reverse proxy handles the expensive SSL handshake and decryption, then forwards plain HTTP to backends over the internal network.

Client ──HTTPS──▶ [Reverse Proxy] ──HTTP──▶ Backend
                  (SSL terminates here)

Benefits:

  • Backends don’t need individual certificates
  • Centralized certificate management (e.g., Let’s Encrypt auto-renewal)
  • Reduced CPU load on application servers

C. Caching & CDN

A reverse proxy can cache static assets (images, CSS, JS) and even API responses. CDNs like Cloudflare and AWS CloudFront are globally distributed reverse proxies.

Client ──▶ CDN Edge (cache HIT) ──▶ Response (fast, no origin hit)
Client ──▶ CDN Edge (cache MISS) ──▶ Origin Server ──▶ Response (cached for next time)

D. Hide Origin Server / DDoS Protection

The public DNS points to the reverse proxy’s IP, not the origin server. Attackers cannot directly target your backend.

Attacker ──▶ Cloudflare IP (proxy) ──▶ filtered traffic ──▶ Origin (hidden IP)

             └── malicious traffic dropped

E. Microservices Routing

A single reverse proxy routes requests to different services based on the URL path.

/api/users   ──▶  User Service    (port 3001)
/api/orders  ──▶  Order Service   (port 3002)
/api/payment ──▶  Payment Service (port 3003)
/            ──▶  Frontend SPA    (port 3000)

This is the foundation of an API Gateway — which we cover next.


5. API & Proxy — The Bodyguard Relationship

An API Gateway is a reverse proxy on steroids. It does everything a reverse proxy does, plus application-layer concerns like authentication, rate limiting, and request transformation.

graph LR
    C[Client] --> GW[API Gateway]
    GW --> US[User Service]
    GW --> OS[Order Service]
    GW --> PS[Payment Service]
    GW --> NS[Notification Service]

    style GW fill:#f9a825,stroke:#f57f17,color:#000

What the API Gateway adds on top of reverse proxy:

FeatureReverse ProxyAPI Gateway
Load Balancing
SSL Termination
Caching
Path-based Routing
Authentication (JWT, OAuth)
Rate Limiting / ThrottlingBasic✅ Advanced
Request/Response Transformation
API Versioning
Analytics & LoggingBasic✅ Detailed

CORS Problem & How Proxy Solves It

During development, your frontend (localhost:3000) calls your API (localhost:8080). The browser blocks this as a cross-origin request.

Solution: Dev proxy — Webpack and Vite can proxy API calls through the dev server so the browser sees same-origin requests.

// vite.config.js
export default {
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
      }
    }
  }
}

Now fetch('/api/users') from the browser hits localhost:3000/api/users, which the Vite dev server proxies to localhost:8080/api/users — no CORS error.


6. Proxy in Serverless & FaaS

What Is FaaS?

Function as a Service (FaaS) lets you deploy individual functions instead of entire servers. The cloud provider manages scaling, patching, and infrastructure.

ProviderFaaS Product
AWSLambda
Google CloudCloud Functions
AzureAzure Functions
CloudflareWorkers

API Gateway as the FaaS Entry Point

Functions don’t listen on ports — they need a trigger. The API Gateway acts as the reverse proxy that receives HTTP requests and “wakes up” the right function.

graph LR
    C[Client] -->|HTTPS| GW[API Gateway]
    GW -->|invoke| F1[Lambda: getUser]
    GW -->|invoke| F2[Lambda: createOrder]
    GW -->|invoke| F3[Lambda: sendEmail]
    F1 -->|response| GW
    F2 -->|response| GW
    F3 -->|response| GW
    GW -->|HTTPS| C

    style GW fill:#42a5f5,stroke:#1565c0,color:#fff

Security & cost control at the gateway level:

  • Rate limiting prevents a single client from triggering millions of invocations (cost explosion)
  • Authentication ensures only authorized users can invoke functions
  • Caching avoids redundant function invocations for identical requests
  • Request validation rejects malformed payloads before they reach your function

7. Proxy in CI/CD — Zero Downtime Deployments

What Is CI/CD?

  • CI (Continuous Integration): Automatically build and test code on every push
  • CD (Continuous Deployment): Automatically deploy tested code to production

The reverse proxy plays a critical role in zero-downtime deployment strategies.

Blue-Green Deployment

Maintain two identical environments. The proxy switches traffic from the old (Blue) to the new (Green) after verification.

                    ┌─────────────────────────────────┐
                    │         Reverse Proxy            │
                    │     (traffic switch)             │
                    └──────────┬──────────────────────┘

              ┌────────────────┼────────────────┐
              ▼                                 ▼
     ┌─────────────────┐              ┌─────────────────┐
     │   BLUE (v1.0)   │              │  GREEN (v2.0)   │
     │   Current prod  │              │  New version    │
     │   ✅ Live       │   ──switch──▶│  ✅ Live        │
     └─────────────────┘              └─────────────────┘

Step 1: Deploy v2.0 to Green (Blue still serves traffic)
Step 2: Run smoke tests on Green
Step 3: Switch proxy to point to Green
Step 4: Blue becomes standby (instant rollback if needed)

Canary Release

Route a small percentage of traffic to the new version. Gradually increase if metrics look healthy.

Reverse Proxy

    ├── 95% ──▶ Stable (v1.0)

    └──  5% ──▶ Canary (v2.0)   ← monitor errors, latency

Nginx weighted upstream example:

upstream backend {
    server 10.0.0.1 weight=95;   # v1.0 — stable
    server 10.0.0.2 weight=5;    # v2.0 — canary
}

If the canary shows no issues, gradually shift: 5% → 25% → 50% → 100%.


8. Proxy Tools Overview

Forward Proxy Tools

ToolTypeNotes
SquidFull-featured forward proxyCaching, ACLs, enterprise-grade
TinyproxyLightweight forward proxyMinimal config, great for labs
Shadowsocks / ClashEncrypted proxy (SOCKS5)Censorship circumvention
Charles / FiddlerHTTP debugging proxyGUI, HTTPS inspection, dev tool

Reverse Proxy Tools

ToolTypeNotes
NginxWeb server + reverse proxyMost popular, also supports forward proxy
Apache (httpd)Web server + reverse proxymod_proxy, long history
HAProxyTCP/HTTP load balancerHigh performance, no static file serving
CaddyWeb server + reverse proxyAutomatic HTTPS (Let’s Encrypt built-in)
TraefikCloud-native reverse proxyAuto-discovers Docker/K8s services

Cloud-Native / Service Mesh

ToolTypeNotes
EnvoyL4/L7 proxyBuilt for microservices, gRPC-native
IstioService mesh (uses Envoy)Sidecar pattern, mTLS, observability
KongAPI GatewayPlugin ecosystem, runs on Nginx/OpenResty
AWS ALB/NLBManaged load balancerIntegrated with AWS ecosystem

Database Proxies

Not all proxies handle HTTP. Database proxies sit between your application and the database to manage connection pooling, read/write splitting, and failover.

ToolDatabaseKey Feature
PgBouncerPostgreSQLConnection pooling (thousands of app connections → few DB connections)
ProxySQLMySQLQuery routing, read/write split, query caching
Amazon RDS ProxyMySQL / PostgreSQLManaged, IAM auth, connection pooling for Lambda

Why it matters: Databases use long-lived connections (unlike HTTP’s short-lived requests). A serverless function (Lambda) that opens a new DB connection per invocation can exhaust the connection pool in seconds. A database proxy solves this by multiplexing many app connections onto a small pool of real DB connections.

Without DB Proxy:
  1000 Lambda invocations → 1000 DB connections → DB crashes (max_connections exceeded)

With DB Proxy:
  1000 Lambda invocations → RDS Proxy → 50 DB connections → DB is happy

Proxy Monitoring & Observability

A proxy you can’t monitor is a proxy you can’t trust. Here are the essentials:

Nginx Monitoring

# Enable stub_status (built-in, basic metrics)
server {
    location /nginx_status {
        stub_status;
        allow 127.0.0.1;
        deny all;
    }
}
# Output:
# Active connections: 42
# server accepts handled requests
#  1234 1234 5678
# Reading: 2 Writing: 5 Waiting: 35

For richer metrics, use the nginx-vts-module or nginx-prometheus-exporter.

Prometheus + Grafana (Industry Standard)

Most modern proxies export metrics in Prometheus format:

ProxyPrometheus Integration
Nginxnginx-prometheus-exporter (scrapes stub_status)
EnvoyBuilt-in /stats/prometheus endpoint
HAProxyBuilt-in Prometheus endpoint (v2.0+)
TraefikBuilt-in --metrics.prometheus flag
Kongprometheus plugin

Key metrics to monitor:

MetricWhy It Matters
Request rate (QPS)Traffic volume, capacity planning
Error rate (5xx %)Backend health, deployment issues
Latency (p50, p95, p99)User experience, SLA compliance
Active connectionsConnection pool saturation
Upstream response timeBackend performance vs proxy overhead

9. Tunneling — ngrok & Alternatives

What Does ngrok Do?

ngrok creates a reverse proxy tunnel from the public internet to your local machine. It gives you a public URL (e.g., https://abc123.ngrok.io) that forwards traffic to localhost:3000.

Internet ──▶ ngrok Edge ──▶ ngrok Tunnel ──▶ Your Laptop (localhost:3000)

Common use cases:

  • Webhook development (Stripe, GitHub, Slack)
  • Demoing local work to clients
  • Testing mobile apps against a local API
  • IoT device callbacks

Alternatives Comparison

ToolFree TierSelf-HostedCustom DomainZero InstallBest For
ngrok✅ (limited)PaidQuick demos, webhook dev
Cloudflare Tunnel✅ (generous)✅ FreeProduction tunnels, free custom domains
FRP✅ (OSS)Full control, own server
Serveo✅ (SSH)Zero-install quick test
PinggyPaid✅ (SSH)Zero-install with more features
Localtunnel❌ (npm)JS developers, quick setup
Tailscale FunnelTeams already using Tailscale VPN

Decision Guide

Need a tunnel?

├── Just testing locally for 5 minutes?
│   └── Serveo or Pinggy (zero install: ssh -R)

├── Webhook development?
│   └── ngrok (best DX, inspect dashboard)

├── Production tunnel with custom domain?
│   └── Cloudflare Tunnel (free, reliable)

├── Need full control / self-hosted?
│   └── FRP (open source, your own server)

└── Team already on Tailscale?
    └── Tailscale Funnel

10. Hands-On Labs

Lab 1: Nginx Reverse Proxy with Load Balancing

Goal: Proxy requests to two backend servers with round-robin load balancing.

/etc/nginx/nginx.conf

http {
    upstream app_servers {
        server 127.0.0.1:3001;
        server 127.0.0.1:3002;
    }

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://app_servers;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

Test it:

# Start two simple backends (in separate terminals)
python3 -m http.server 3001 --directory ./site-a
python3 -m http.server 3002 --directory ./site-b

# Reload Nginx
sudo nginx -t && sudo nginx -s reload

# Hit the proxy — requests alternate between 3001 and 3002
curl -i http://localhost

Lab 2: Cloudflare Tunnel Setup

Goal: Expose localhost:3000 to the internet with a custom domain — no port forwarding, no public IP needed.

# 1. Install cloudflared
# macOS
brew install cloudflared

# Linux (Debian/Ubuntu)
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
sudo dpkg -i cloudflared.deb

# 2. Authenticate with your Cloudflare account
cloudflared tunnel login

# 3. Create a tunnel
cloudflared tunnel create my-app

# 4. Configure the tunnel
cat > ~/.cloudflared/config.yml << 'EOF'
tunnel: my-app
credentials-file: /home/user/.cloudflared/<TUNNEL_ID>.json

ingress:
  - hostname: app.yourdomain.com
    service: http://localhost:3000
  - service: http_status:404
EOF

# 5. Add DNS record (CNAME to tunnel)
cloudflared tunnel route dns my-app app.yourdomain.com

# 6. Run the tunnel
cloudflared tunnel run my-app

Now https://app.yourdomain.com routes to your local localhost:3000 through Cloudflare’s network.

Lab 3: Tinyproxy Forward Proxy

Goal: Set up a forward proxy and verify your IP changes.

# Install
sudo apt install tinyproxy

# Configure — edit /etc/tinyproxy/tinyproxy.conf
# Key settings:
#   Port 8888
#   Allow 127.0.0.1        (allow localhost)
#   Allow 192.168.1.0/24   (allow your LAN)

# Start
sudo systemctl start tinyproxy

# Test — your request goes through the proxy
curl -x http://localhost:8888 https://httpbin.org/ip
# Response shows the proxy server's IP, not yours

# Compare with direct request
curl https://httpbin.org/ip
# Response shows your real IP

Lab 4: Docker Compose + Nginx API Gateway

Goal: Run three microservices behind Nginx as an API Gateway.

docker-compose.yml

version: "3.8"

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - user-service
      - order-service
      - product-service

  user-service:
    image: node:20-alpine
    working_dir: /app
    volumes:
      - ./services/user:/app
    command: node index.js
    expose:
      - "3001"

  order-service:
    image: node:20-alpine
    working_dir: /app
    volumes:
      - ./services/order:/app
    command: node index.js
    expose:
      - "3002"

  product-service:
    image: node:20-alpine
    working_dir: /app
    volumes:
      - ./services/product:/app
    command: node index.js
    expose:
      - "3003"

nginx.conf

events { worker_connections 1024; }

http {
    server {
        listen 80;

        # User Service
        location /api/users {
            proxy_pass http://user-service:3001;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        # Order Service
        location /api/orders {
            proxy_pass http://order-service:3002;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        # Product Service
        location /api/products {
            proxy_pass http://product-service:3003;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        # Health check
        location /health {
            return 200 '{"status": "ok"}';
            add_header Content-Type application/json;
        }
    }
}

Run and test:

docker compose up -d

curl http://localhost/api/users
curl http://localhost/api/orders
curl http://localhost/api/products
curl http://localhost/health

All four URLs hit port 80 on Nginx, which routes each path to the correct microservice container.

Consider Traefik for Docker-heavy setups. While Nginx requires manual proxy_pass configuration for each service, Traefik auto-discovers Docker containers via labels — no config file updates needed when you add or remove services.

# With Traefik, each service declares its own routing via labels:
services:
  user-service:
    image: my-user-service
    labels:
      - "traefik.http.routers.users.rule=PathPrefix(`/api/users`)"
      - "traefik.http.services.users.loadbalancer.server.port=3001"

Traefik watches the Docker socket and updates routing in real time — ideal for dynamic environments where containers scale up and down frequently.


11. HTTP Version Differences in Proxy Context

Proxies sit in the middle of every HTTP conversation, so the HTTP version matters a lot for performance.

HTTP/1.1 — The Head-of-Line Blocking Problem

HTTP/1.1 uses one request per TCP connection at a time. If a response is slow, everything behind it waits.

Connection 1:  [──── Request A (slow) ────][── Request B ──][── Request C ──]

                                     B waits for A to finish
                                     = Head-of-Line Blocking

Browsers work around this by opening 6–8 parallel TCP connections, but each connection still suffers from HOL blocking.

HTTP/2 — Multiplexing Solves HOL Blocking

HTTP/2 sends multiple requests over a single TCP connection as interleaved binary frames.

Single Connection:  [A-frame][B-frame][A-frame][C-frame][B-frame][A-frame]

                     All three requests progress simultaneously
                     = No Head-of-Line Blocking (at HTTP layer)

Note: TCP-level HOL blocking still exists — if a packet is lost, all streams on that connection stall while TCP retransmits.

HTTP/3 — QUIC Eliminates TCP HOL Blocking

HTTP/3 replaces TCP with QUIC (UDP-based). Each stream is independent — a lost packet on Stream A does not block Stream B.

HTTP/3 over QUIC:
  Stream A: [frame][frame]...[lost]...[retransmit]
  Stream B: [frame][frame][frame]  ← unaffected by A's loss
  Stream C: [frame][frame]         ← unaffected by A's loss

Comparison Table

FeatureHTTP/1.1HTTP/2HTTP/3
TransportTCPTCPQUIC (UDP)
Multiplexing❌ (1 req/conn)✅ (streams)✅ (streams)
HTTP HOL Blocking✅ Yes❌ Solved❌ Solved
TCP HOL Blocking✅ Yes✅ Still exists❌ Solved
Header CompressionHPACKQPACK
Connection SetupTCP + TLS (2-3 RTT)TCP + TLS (2-3 RTT)0-1 RTT
Server Push
EncryptionOptionalPractically requiredAlways (built into QUIC)

Proxy Implications

  • Nginx supports HTTP/2 to clients since v1.9.5, and HTTP/3 (QUIC) experimentally since v1.25. However, Nginx still uses HTTP/1.1 to backends by default (proxy_http_version 1.1;).
  • Envoy supports HTTP/2 and gRPC natively to both clients and backends — this is why modern service meshes prefer Envoy over Nginx for east-west traffic (see §14).
  • HAProxy added HTTP/2 backend support in v2.0+.

gRPC — Why HTTP/2 Matters for Microservices

gRPC is Google’s high-performance RPC framework, and it requires HTTP/2. This has major proxy implications:

  • gRPC uses HTTP/2 multiplexing to send multiple RPC calls over a single connection
  • gRPC uses HTTP/2 trailers for status codes (not all proxies support trailers)
  • gRPC supports bidirectional streaming — only possible with HTTP/2’s stream model
gRPC call flow through a proxy:

Client ──HTTP/2──▶ [Proxy] ──HTTP/2──▶ gRPC Service

                     └── Proxy MUST support HTTP/2 on both sides
                         (Nginx added this in v1.13.10, Envoy had it from day one)

This is a key reason Envoy became the default proxy in service meshes like Istio — it supported full HTTP/2 and gRPC proxying from the start, while Nginx required workarounds for years. Today Nginx handles gRPC well with grpc_pass, but Envoy remains the go-to for gRPC-heavy architectures.

# Enable HTTP/2 in Nginx (client-facing)
server {
    listen 443 ssl http2;
    # ...
}

# Force HTTP/1.1 to backend (Nginx default, but explicit is better)
location / {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

12. Security Considerations

Forward Proxy Risks

Open Proxy as Attack Relay

An open proxy is a forward proxy that accepts connections from anyone on the internet. Attackers use open proxies to:

  • Hide their identity while attacking other servers
  • Send spam email
  • Scrape websites without being traced

Prevention: Always restrict access with authentication or IP allowlists.

# Squid ACL example — only allow your network
acl localnet src 192.168.1.0/24
http_access allow localnet
http_access deny all

Reverse Proxy Benefits

BenefitHow
Hide origin IPDNS points to proxy, not backend
DDoS shieldProxy absorbs volumetric attacks
WAF integrationInspect and block malicious payloads (SQLi, XSS)
Bot mitigationChallenge suspicious traffic (CAPTCHA, JS challenge)

WAF — The Reverse Proxy with a Rule Engine

A Web Application Firewall (WAF) is essentially a reverse proxy that inspects every request payload at Layer 7 and matches it against attack signatures.

Client ──▶ [WAF / Reverse Proxy] ──▶ Backend

                ├── "SELECT * FROM users WHERE 1=1" → BLOCKED (SQLi)
                ├── "<script>alert('xss')</script>"  → BLOCKED (XSS)
                └── Normal request                   → ALLOWED

Common WAF solutions:

  • Cloudflare WAF — managed, integrated with CDN
  • AWS WAF — rule-based, works with ALB/CloudFront
  • ModSecurity — open-source WAF module for Nginx/Apache (OWASP Core Rule Set)
# ModSecurity with Nginx example
load_module modules/ngx_http_modsecurity_module.so;

server {
    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/main.conf;
    # ...
}

SSL/TLS Best Practices

server {
    listen 443 ssl http2;

    # Use modern TLS only
    ssl_protocols TLSv1.2 TLSv1.3;

    # Strong cipher suites
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers on;

    # HSTS — tell browsers to always use HTTPS
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;

    # OCSP Stapling — faster certificate validation
    ssl_stapling on;
    ssl_stapling_verify on;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}

SSRF & Header Injection Awareness

Server-Side Request Forgery (SSRF): If your proxy accepts user-supplied URLs (e.g., a URL preview feature), an attacker can make the proxy fetch internal resources.

Attacker sends: GET /fetch?url=http://169.254.169.254/latest/meta-data/

                    AWS metadata endpoint — leaks IAM credentials

Mitigations:

  • Validate and sanitize user-supplied URLs
  • Block requests to private IP ranges (10.x, 172.16.x, 192.168.x, 169.254.x)
  • Use allowlists for permitted domains
  • Run the proxy in a network namespace without access to internal services

13. Troubleshooting

Reading Nginx Error Logs

# Default log locations
tail -f /var/log/nginx/error.log
tail -f /var/log/nginx/access.log

# Custom log level (in nginx.conf)
error_log /var/log/nginx/error.log warn;   # levels: debug, info, notice, warn, error, crit

502 Bad Gateway

Meaning: The proxy connected to the backend, but the backend returned an invalid response (or crashed).

Common causes & fixes:

CauseFix
Backend is downsystemctl status your-app — restart if needed
Wrong upstream portVerify proxy_pass matches the backend’s actual port
Backend crashed mid-responseCheck application logs for unhandled exceptions
PHP-FPM socket not foundVerify fastcgi_pass path matches FPM config
# Quick diagnosis
curl -v http://localhost:3001/health    # Can you reach the backend directly?
sudo ss -tlnp | grep 3001              # Is the backend listening?
journalctl -u your-app --since "5 min ago"  # Recent app logs

504 Gateway Timeout

Meaning: The proxy waited too long for the backend to respond.

Common causes & fixes:

CauseFix
Slow database queryOptimize the query, add indexes
Backend processing too longIncrease proxy_read_timeout (temporary fix)
Network issue between proxy and backendCheck connectivity, DNS resolution
Upstream keepalive exhaustedTune keepalive in upstream block
# Increase timeouts (use as temporary relief, not permanent fix)
location /api {
    proxy_pass http://backend;
    proxy_connect_timeout 10s;
    proxy_send_timeout    30s;
    proxy_read_timeout    60s;
}

Common Misconfigurations

MistakeSymptomFix
Missing trailing slash in proxy_passWrong path forwardedproxy_pass http://backend/; (note the /)
Not passing Host headerBackend sees wrong hostnameAdd proxy_set_header Host $host;
Not passing real client IPLogs show proxy IP onlyAdd X-Real-IP and X-Forwarded-For headers
proxy_http_version not setWebSocket failsSet proxy_http_version 1.1; + Upgrade headers
SELinux blocking connections502 on CentOS/RHELsetsebool -P httpd_can_network_connect 1

Debug Checklist

□ Can you curl the backend directly? (bypass proxy)
□ Is the backend process running? (ps, systemctl, docker ps)
□ Is the backend listening on the expected port? (ss -tlnp)
□ Does nginx -t pass? (config syntax check)
□ Are the logs showing errors? (error.log, app logs)
□ Is there a firewall blocking the connection? (iptables, ufw, security groups)
□ Is DNS resolving correctly? (dig, nslookup)
□ Are file permissions correct? (socket files, SSL certs)

14. Architecture Patterns (Advanced)

SSH Tunneling — DIY ngrok

SSH can create tunnels without any third-party tools.

Local Port Forwarding (ssh -L)

Access a remote service through a local port. Useful when a database is behind a firewall.

Your PC (localhost:5432) ──SSH──▶ Jump Server ──▶ DB Server (10.0.0.5:5432)
ssh -L 5432:10.0.0.5:5432 user@jump-server.com
# Now: psql -h localhost -p 5432 connects to the remote DB

Remote Port Forwarding (ssh -R)

Expose your local service on a remote server’s port. This is essentially what ngrok does.

Internet ──▶ Remote Server (port 8080) ──SSH──▶ Your PC (localhost:3000)
ssh -R 8080:localhost:3000 user@your-vps.com
# Now: http://your-vps.com:8080 reaches your local port 3000

Zero Trust Network Access (ZTNA)

Traditional VPNs grant access to the entire network once connected. Zero Trust verifies every request individually.

Traditional VPN:
  User ──VPN──▶ [Inside Network] ──▶ Access EVERYTHING

Zero Trust:
  User ──▶ Identity Check ──▶ Device Check ──▶ Access ONLY authorized app

Tools:

  • Cloudflare Access — identity-aware proxy in front of internal apps
  • Tailscale — WireGuard-based mesh VPN with ACLs per service
  • Google BeyondCorp — Google’s internal zero-trust implementation

Service Mesh & Sidecar Pattern

As microservices grow, managing proxy rules centrally becomes a bottleneck. The sidecar pattern places a proxy next to every service.

Diagram A: Centralized Proxy (Traditional)

                    ┌──────────────────┐
                    │   Nginx / HAProxy │
                    │   (single point)  │
                    └────────┬─────────┘

              ┌──────────────┼──────────────┐
              ▼              ▼              ▼
         ┌─────────┐   ┌─────────┐   ┌─────────┐
         │ Service │   │ Service │   │ Service │
         │    A    │   │    B    │   │    C    │
         └─────────┘   └─────────┘   └─────────┘

All traffic funnels through ONE proxy.
Simple, but single point of failure.

Diagram B: Sidecar Proxy (Service Mesh)

         ┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐
         │  Pod A           │  │  Pod B           │  │  Pod C           │
         │ ┌──────┐ ┌─────┐│  │ ┌──────┐ ┌─────┐│  │ ┌──────┐ ┌─────┐│
         │ │Envoy │◀▶│ Svc ││  │ │Envoy │◀▶│ Svc ││  │ │Envoy │◀▶│ Svc ││
         │ │proxy │ │  A  ││  │ │proxy │ │  B  ││  │ │proxy │ │  C  ││
         │ └──┬───┘ └─────┘│  │ └──┬───┘ └─────┘│  │ └──┬───┘ └─────┘│
         └────┼────────────┘  └────┼────────────┘  └────┼────────────┘
              │                    │                    │
              └────────────────────┴────────────────────┘
                          mesh network (mTLS)

Every service gets its OWN proxy.
No single point of failure. Distributed observability.

Istio architecture:

  • Data plane: Envoy sidecars handle all service-to-service traffic
  • Control plane: Istiod manages configuration, certificates, and policies
  • Benefits: mTLS everywhere, distributed tracing, traffic shaping, fault injection

Evolution Path

§4 Centralized Reverse Proxy (Nginx at the front gate)

§5 API Gateway (reverse proxy + auth, rate limiting)

§14 Service Mesh (sidecar proxy per service, full observability)

Docker Networking with Proxies

Docker containers communicate through virtual networks. A reverse proxy container connects to the same Docker network as the backend containers.

# Create a shared network
docker network create app-net

# Run backend
docker run -d --name api --network app-net my-api-image

# Run Nginx proxy on the same network
docker run -d --name proxy --network app-net -p 80:80 \
  -v ./nginx.conf:/etc/nginx/nginx.conf:ro nginx:alpine

# In nginx.conf, use the container name as hostname:
#   proxy_pass http://api:3000;

Docker’s built-in DNS resolves container names to their internal IPs within the same network.


15. Summary

Forward vs Reverse — Quick Reference

Forward ProxyReverse Proxy
RepresentsClientServer
Client aware?Yes (configured by client)No (transparent to client)
HidesClient’s IPServer’s IP
Deployed byUser / organizationService provider
ExamplesVPN, Squid, CharlesNginx, Cloudflare, HAProxy
Use casesPrivacy, filtering, debuggingLoad balancing, SSL, caching

Tool Selection Decision Tree

What do you need?

├── Intercept & debug HTTP traffic?
│   └── Charles / Fiddler (forward proxy)

├── Hide your IP / bypass geo-blocks?
│   └── VPN or Shadowsocks (forward proxy)

├── Serve a website with load balancing?
│   ├── Simple setup → Caddy (auto HTTPS)
│   ├── Full control → Nginx
│   └── Maximum performance → HAProxy

├── API Gateway with auth & rate limiting?
│   ├── Self-hosted → Kong
│   ├── Cloud-native → AWS API Gateway
│   └── Kubernetes → Traefik or Envoy

├── Expose localhost to the internet?
│   ├── Quick test → ngrok or Serveo
│   ├── Production → Cloudflare Tunnel
│   └── Self-hosted → FRP

├── Microservices with service mesh?
│   └── Istio + Envoy (sidecar pattern)

└── Zero Trust access to internal apps?
    └── Cloudflare Access or Tailscale

Key Takeaways

  1. Forward proxy = client’s agent. It hides the client and controls outbound traffic.
  2. Reverse proxy = server’s agent. It hides the backend and controls inbound traffic.
  3. API Gateways are reverse proxies with superpowers (auth, rate limiting, transformation).
  4. Tunneling tools (ngrok, Cloudflare Tunnel) are reverse proxies that bridge local dev to the public internet.
  5. CI/CD deployments (Blue-Green, Canary) rely on the proxy as the traffic switch for zero-downtime releases.
  6. HTTP/2 and HTTP/3 solve head-of-line blocking — modern proxies like Envoy leverage this for better performance.
  7. Service meshes evolve the proxy from a centralized gateway to a distributed sidecar beside every service.
  8. Security is non-negotiable — never run an open proxy, always terminate TLS properly, and watch for SSRF.