反向代理
一行版本
章节「一行版本」app.example.com { reverse_proxy localhost:3000}WebSocket、gRPC、SSE、长连接都自动处理。真的,就这一行。
负载均衡
章节「负载均衡」app.example.com { reverse_proxy localhost:3000 localhost:3001 localhost:3002}默认策略是轮询。健康检查会自动剔除故障节点——这是它比 Nginx 默认配置省事的地方。
主动健康检查
章节「主动健康检查」app.example.com { reverse_proxy localhost:3000 { health_uri /healthz health_interval 30s health_timeout 5s }}health_interval 建议 10~60s,太短会给后端制造无谓的压力。
故障转移与重试
章节「故障转移与重试」reverse_proxy { to localhost:3000 localhost:3001 lb_policy round_robin lb_retries 3 lb_try_duration 5s fail_duration 30s max_fails 3}| 选项 | 作用 |
|---|---|
lb_retries |
单个上游失败后重试次数 |
lb_try_duration |
切换到下一个上游前的等待窗口 |
fail_duration |
节点被摘除的持续时间 |
max_fails |
触发摘除的失败阈值 |
负载均衡策略
章节「负载均衡策略」lb_policy <策略>| 策略 | 说明 |
|---|---|
random |
随机选两个里负载更低的 |
round_robin |
轮询(默认) |
least_conn |
选连接数最少的 |
ip_hash |
按客户端 IP 哈希,会话保持 |
header |
按指定请求头哈希,粘性会话常用 |
cookie |
按 cookie 哈希 |
会话保持:
lb_policy cookie SRVID 10m路径前缀剥离
章节「路径前缀剥离」后端不需要知道 /api 前缀存在:
handle_path /api/* { reverse_proxy localhost:8080}/api/users?page=2 → 后端收到 /users?page=2。
如果你只想去掉 /api 而保留 query,或者想在反代后重写,用rewrite:
@api path /api/*rewrite @api /{uri}reverse_proxy localhost:8080修改上游请求
章节「修改上游请求」reverse_proxy localhost:3000 { # 传给后端的请求头 header_up Host {http.request.host} header_up X-Real-IP {http.request.remote.ip} header_up X-Forwarded-Proto {http.request.scheme}
# 删除某个头 header_up -Accept-Encoding}修改上游响应
章节「修改上游响应」reverse_proxy localhost:3000 { # 添加响应头 header X-Cache-Status MISS
# 删除后端泄漏的内部头 header -X-Powered-By header -Server}流式与长连接
章节「流式与长连接」SSE / 长轮询需要关掉缓冲:
reverse_proxy localhost:3000 { flush_interval -1 # 每收到一块就立刻转发 transport http { dial_timeout 10s response_header_timeout 0 # 不限制响应头等待时间 }}flush_interval -1 是低延迟流式响应的标准写法。不加的话内容会被攒着一起发,流式效果直接消失。
反代到 Docker 容器
章节「反代到 Docker 容器」reverse_proxy app:3000Caddy 在同一个 Docker 网络里会用服务名做 DNS 解析,不用关心容器 IP。
宿主机场景用 host.docker.internal 而不是 localhost——容器里的 localhost 是容器自己,这个错误非常常见。
常见问题
章节「常见问题」| 现象 | 原因 |
|---|---|
| 502 Bad Gateway | 后端没起来、端口不对、监听在 127.0.0.1 而容器外访问不到 |
| WebSocket 连上就断 | 反代后面套了另一个会超时的代理 |
| 大文件上传 413 | request_body 限制,容器内反向场景还要看下游限制 |
| SSE 半天不出数据 | 忘了 flush_interval -1 |
| 301/302 循环 | 上游返回了 http 的 Location,但外层强制 https |