Nginx 报 upstream timed out 但直连后端很快,主因是 keepalive 连接复用失效:Nginx 与后端超时配置错位(如 Tomcat 默认 20s 关闭空闲连接而 Nginx 未同步),或缺少 proxy_set_header Connection '' 等关键配置导致复用未生效。

为什么 upstream timed out 却查不到后端慢?
Nginx 报 upstream timed out,但用 curl 或 Postman 直连后端服务响应很快(比如 20ms),说明问题不在后端处理逻辑本身,而大概率出在连接复用或超时配置不匹配上。常见原因是:Nginx 与后端建立的 keepalive 连接被后端主动关闭,而 Nginx 还在等待读写;或者 Nginx 的超时值比后端的连接空闲关闭时间更长,导致复用时拿到一个已失效的 socket。
关键点在于:keepalive 是双向的——既要 Nginx 主动维护连接池,也要后端允许并维持长连接。两者超时参数若错位,就会出现“连接看似活着、实则不可用”的状态。
keepalive 指令必须配对使用
Nginx 的 upstream keepalive 不是单个指令,而是三个参数共同生效:
-
keepalive 32;:定义每个 worker 进程最多缓存多少个空闲 keepalive 连接(不是总连接数)
-
proxy_http_version 1.1;:必须显式开启 HTTP/1.1,否则默认用 1.0,无法复用连接
-
proxy_set_header Connection '';:清空客户端传来的 Connection: close,避免干扰复用
keepalive 32;:定义每个 worker 进程最多缓存多少个空闲 keepalive 连接(不是总连接数)proxy_http_version 1.1;:必须显式开启 HTTP/1.1,否则默认用 1.0,无法复用连接proxy_set_header Connection '';:清空客户端传来的 Connection: close,避免干扰复用漏掉任意一项,keepalive 都不会真正生效。尤其容易忽略的是 proxy_set_header Connection ''——很多默认模板没加这行,结果 upstream 总是新建连接,根本走不到复用逻辑。
超时参数要分层对齐后端行为
Nginx 有多个超时控制,各自作用不同,不能只调大 proxy_read_timeout:
-
proxy_connect_timeout 5s;:仅控制与后端建连阶段(TCP 握手 + TLS),不涉及请求发送
-
proxy_send_timeout 30s;:控制 Nginx 向后端发完请求后,等待后端接收完成的超时(极少触发,除非后端网卡满或缓冲区堵)
-
proxy_read_timeout 60s;:控制 Nginx 从后端读响应体的超时,适用于流式响应或大文件下载
-
keepalive_timeout 60s;(在 upstream 外):控制 Nginx 与客户端的 keepalive 超时,和 upstream 无关
proxy_connect_timeout 5s;:仅控制与后端建连阶段(TCP 握手 + TLS),不涉及请求发送proxy_send_timeout 30s;:控制 Nginx 向后端发完请求后,等待后端接收完成的超时(极少触发,除非后端网卡满或缓冲区堵)proxy_read_timeout 60s;:控制 Nginx 从后端读响应体的超时,适用于流式响应或大文件下载keepalive_timeout 60s;(在 upstream 外):控制 Nginx 与客户端的 keepalive 超时,和 upstream 无关真正影响 keepalive 复用的关键是后端的空闲超时。例如:Tomcat 默认 connectionTimeout=20000(20s),而 Nginx 的 keepalive 32 连接若闲置超过 20s,Tomcat 就会断开,但 Nginx 不知道,下次复用时就报 upstream timed out (110: Connection timed out)。
解决办法:把后端的 keepalive timeout 设为 > Nginx 的 proxy_read_timeout,同时确保 Nginx 的 keepalive_requests(默认 100)不设得过大,避免单连接压太久出错。
如何验证 keepalive 是否真在工作
光看日志不够,得抓底层连接状态:
- 用
ss -tnp | grep :8080(假设后端在 8080)观察 ESTAB 状态连接数是否稳定,而不是每次请求都涨
- 在 upstream 块里加
keepalive_requests 1000; 并配合 log_format 打印 $upstream_addr,看相同 IP:PORT 是否被反复复用
- 临时加
proxy_next_upstream error timeout http_502; 和 proxy_next_upstream_tries 2;,能掩盖一次失效连接,但只是兜底,不是根治
ss -tnp | grep :8080(假设后端在 8080)观察 ESTAB 状态连接数是否稳定,而不是每次请求都涨keepalive_requests 1000; 并配合 log_format 打印 $upstream_addr,看相同 IP:PORT 是否被反复复用proxy_next_upstream error timeout http_502; 和 proxy_next_upstream_tries 2;,能掩盖一次失效连接,但只是兜底,不是根治最直接的证据:Nginx error log 出现 upstream sent no valid HTTP/1.0 header 或 recv() failed (104: Connection reset by peer),基本可锁定是 keepalive 连接被后端静默关闭所致。
调优没有银弹——后端语言、框架、代理层(如 Spring Cloud Gateway)、甚至容器网络插件(如 Cilium 对 idle connection 的干预)都会影响 keepalive 行为。先确认后端实际关闭连接的时间点,再反推 Nginx 参数,比盲目调大 timeout 更有效。










