跳到正文

反向代理

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 是低延迟流式响应的标准写法。不加的话内容会被攒着一起发,流式效果直接消失。

reverse_proxy app:3000

Caddy 在同一个 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