---
title: 反向代理与负载均衡
url: https://doc.liz6.com/networking/06-HTTP/04-reverse-proxy-and-load-balancing
locale: zh
area: networking
tags:
- networking
- HTTP
date: 2026-06-30
modified: 2026-07-11
description: 反向代理不只是"转发请求"——TLS 终结、缓存、压缩、限流、健康检查、蓝绿发布,现代架构的流量管理全集中在这一层。nginx/Caddy/HAProxy 的配置哲学各有不同。
---

# 反向代理与负载均衡

> 反向代理不只是"转发请求"——TLS 终结、缓存、压缩、限流、健康检查、蓝绿发布,现代架构的流量管理全集中在这一层。nginx/Caddy/HAProxy 的配置哲学各有不同。

## 概述

反向代理是服务器端的门面——它接收所有外部请求，做 TLS 终止、SNI 路由、限流和日志，将请求转发到后端服务。负载均衡器将请求按策略（轮询/最少连接/IP hash）分发到多个后端实例，提高可用性和吞吐。Caddy 以自动 TLS（ACME + Let's Encrypt 零配置）著称，nginx/HAProxy 以高性能和丰富的 L4/L7 功能主导企业环境。理解反向代理是部署任何互联网服务的前提。

## 反向代理 vs 正向代理

<svg viewBox="0 0 720 320" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="正向代理与反向代理的请求流向对比">
  <defs><marker id="rpah" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker></defs>
  <rect width="720" height="320" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">正向代理 vs 反向代理:谁在代理谁</text>

  <text x="40" y="54" font-size="13" font-weight="700" fill="#334155">正向代理</text>
  <rect x="40" y="62" width="80" height="34" rx="6" fill="#e2e8f0"/>
  <text x="80" y="83" text-anchor="middle" font-size="12" fill="#334155">Client</text>
  <line x1="120" y1="79" x2="156" y2="79" stroke="#475569" stroke-width="1.6" marker-end="url(#rpah)"/>
  <rect x="160" y="62" width="80" height="34" rx="6" fill="#cbd5e1"/>
  <text x="200" y="83" text-anchor="middle" font-size="12" fill="#334155">Proxy</text>
  <line x1="240" y1="79" x2="276" y2="79" stroke="#475569" stroke-width="1.6" marker-end="url(#rpah)"/>
  <rect x="280" y="62" width="90" height="34" rx="6" fill="#e2e8f0"/>
  <text x="325" y="83" text-anchor="middle" font-size="12" fill="#334155">Target</text>
  <text x="40" y="114" font-size="11" fill="#64748b">例:本机 mihomo :7890 → 代理浏览器流量到国外</text>

  <line x1="40" y1="130" x2="680" y2="130" stroke="#e2e8f0" stroke-width="1"/>

  <text x="40" y="150" font-size="13" font-weight="700" fill="#0f766e">反向代理</text>
  <rect x="40" y="160" width="70" height="36" rx="6" fill="#ccfbf1"/>
  <text x="75" y="182" text-anchor="middle" font-size="11" fill="#115e59">Client</text>
  <line x1="110" y1="178" x2="146" y2="178" stroke="#475569" stroke-width="1.6" marker-end="url(#rpah)"/>
  <rect x="150" y="160" width="170" height="36" rx="6" fill="#f0fdfa" stroke="#99f6e4" stroke-dasharray="4 3"/>
  <text x="235" y="175" text-anchor="middle" font-size="11" fill="#0f766e">grafana.liz6.com</text>
  <text x="235" y="190" text-anchor="middle" font-size="9.5" fill="#0f766e">(看起来是 Target)</text>
  <line x1="320" y1="178" x2="356" y2="178" stroke="#475569" stroke-width="1.6" marker-end="url(#rpah)"/>
  <text x="338" y="170" text-anchor="middle" font-size="9.5" fill="#64748b">其实是</text>
  <rect x="360" y="160" width="110" height="36" rx="6" fill="#0d9488"/>
  <text x="415" y="175" text-anchor="middle" font-size="11" font-weight="600" fill="#ffffff">Caddy:443</text>
  <text x="415" y="190" text-anchor="middle" font-size="9.5" fill="#ffffff">(Proxy)</text>
  <line x1="470" y1="178" x2="506" y2="178" stroke="#475569" stroke-width="1.6" marker-end="url(#rpah)"/>
  <rect x="510" y="160" width="140" height="36" rx="6" fill="#ccfbf1" stroke="#99f6e4"/>
  <text x="580" y="175" text-anchor="middle" font-size="11" fill="#115e59">127.0.0.1:3000</text>
  <text x="580" y="190" text-anchor="middle" font-size="9.5" fill="#115e59">(Backend)</text>
  <text x="40" y="214" font-size="11" fill="#64748b">Client 以为自己直接连的是 Grafana,不知道它在 localhost 上</text>

  <rect x="40" y="236" width="640" height="56" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="56" y="258" font-size="12.5" fill="#115e59">正向代理帮 Client 访问外网、对 Target 隐藏 Client;反向代理反过来,</text>
  <text x="56" y="278" font-size="12.5" fill="#115e59">对 Client 伪装成 Target 本身,把真正的 Backend 藏在身后。</text>
</svg>

## Caddy 配置

```caddy
grafana.liz6.com {
    reverse_proxy 127.0.0.1:3000 {
        header_up Host {upstream_hostport}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }
}

chat.liz6.com {
    reverse_proxy 127.0.0.1:3001
}
```

反向代理的职责:
1. **TLS Termination**: Caddy 做 TLS → 后端 HTTP → 后端不需要处理 TLS
2. **SNI Routing**: 同一 port (443) 根据 SNI 路由到不同后端
3. **Request rewriting**: 添加 X-Forwarded-*, 修改 path/headers
4. **Rate Limiting**: per-client or per-endpoint rate limiting
5. **Access Log**: 集中日志 (Caddy JSON access log)

## L4 vs L7 Load Balancing

<svg viewBox="0 0 720 360" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="L4 与 L7 负载均衡的原理对比">
  <rect width="720" height="360" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">L4 vs L7:解不解析 HTTP,决定路由能力和速度</text>

  <rect x="40" y="44" width="310" height="26" rx="6" fill="#94a3b8"/>
  <text x="195" y="61" text-anchor="middle" font-size="13" font-weight="700" fill="#ffffff">L4 · TCP(不解析 HTTP)</text>
  <rect x="40" y="80" width="310" height="40" rx="6" fill="#f1f5f9" stroke="#e2e8f0"/>
  <text x="52" y="104" font-size="10.5" fill="#475569">nginx stream module / HAProxy mode tcp</text>
  <rect x="40" y="130" width="310" height="56" rx="6" fill="#f1f5f9" stroke="#e2e8f0"/>
  <text x="52" y="152" font-size="11" font-weight="600" fill="#334155">不解析 HTTP → 只看 TCP payload → 但非常快</text>
  <text x="52" y="172" font-size="10.5" fill="#64748b">不能按 URL / Host header 路由</text>
  <rect x="40" y="196" width="310" height="48" rx="6" fill="#dcfce7" stroke="#4ade80"/>
  <text x="52" y="216" font-size="11" font-weight="700" fill="#166534">优势:非常快</text>
  <text x="52" y="234" font-size="10.5" fill="#15803d">适合 raw TCP 转发、WebSocket pass-through</text>

  <rect x="370" y="44" width="310" height="26" rx="6" fill="#4f46e5"/>
  <text x="525" y="61" text-anchor="middle" font-size="13" font-weight="700" fill="#ffffff">L7 · HTTP(解析并按内容路由)</text>
  <rect x="370" y="80" width="310" height="40" rx="6" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="382" y="104" font-size="10.5" fill="#3730a3">nginx http module / HAProxy mode http / Caddy</text>
  <rect x="370" y="130" width="310" height="72" rx="6" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="382" y="148" font-size="10.5" font-weight="600" fill="#3730a3">解析 HTTP → 可按 Host / URL /</text>
  <text x="382" y="164" font-size="10.5" font-weight="600" fill="#3730a3">Header / Cookie / Method 路由</text>
  <text x="382" y="182" font-size="10" fill="#4338ca">可做 content-based 限流、请求检查</text>
  <rect x="370" y="212" width="310" height="48" rx="6" fill="#ffedd5" stroke="#f97316"/>
  <text x="382" y="232" font-size="11" font-weight="700" fill="#9a3412">代价:略慢</text>
  <text x="382" y="250" font-size="10.5" fill="#c2410c">HTTP parsing overhead</text>

  <rect x="40" y="282" width="640" height="56" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="56" y="304" font-size="12.5" fill="#3730a3">选 L4 还是 L7,看要不要按内容路由:纯转发、WebSocket 透传用 L4 求快;</text>
  <text x="56" y="324" font-size="12.5" fill="#3730a3">要按 Host / Path / Header 分流、做内容级限流,就必须上 L7。</text>
</svg>

## 负载均衡算法

```
Round Robin:         逐 backend 轮询
Least Connections:   选 conns 最少的 backend (只对 L7 有效)
Random:              随机
IP Hash:             同一 client IP → 同一 backend → sticky
Consistent Hash:     hash ring → backend 增减时只有 1/N 重新 hash
Weighted:            backend 权重 → weight=3 多 3x 流量

Health Check:
  passive: 从实际 request 的 failure 检测 (max_fails=N)
  active:  定期 GET /health → 期望 200 → 标记为 down on failure
  slow_start: backend 恢复后逐步增加流量 (避免 flood)
```

## 参考

- **Caddy**: caddyserver.com/docs/caddyfile/directives/reverse_proxy
- **nginx**: nginx.org/en/docs/http/ngx_http_upstream_module.html
- **HAProxy**: haproxy.org

*Keywords: reverse proxy, SNI routing, L4/L7 LB, health check, sticky session, rate limiting, Caddy*
