Q先生的世界

面朝大海,春暖花开

关于Nginx中的各种timeout设置

很多人第一次看 Nginx 配置里的各种 timeout,会有一种错觉:

  1. 好像都是“超时时间”
  2. 名字只差一个单词
  3. 调大一点似乎总没坏处

结果线上一出问题,大家就开始机械地把 60s 改成 600s,然后继续观察。

这类处理方式通常不太可靠。因为 Nginx 里的 timeout 并不是一个参数的不同别名,而是分布在不同阶段、不同方向、不同对象上的控制开关。你如果没搞清楚“是谁在等谁”“在等什么”“是总时长还是两次读写之间的空档”,调出来的值往往既不能解决问题,还可能把连接、内存和文件描述符长期占住。

这篇文章就专门把这个问题讲透。我们会从 Nginx 请求链路入手,系统梳理:

  1. 客户端到 Nginx 的 timeout
  2. Nginx 到上游服务的 timeout
  3. 长连接与 keepalive 的 timeout
  4. DNS 解析相关的 timeout
  5. 常见报错码背后通常是哪一类 timeout
  6. 上传、大响应、长轮询、WebSocket 等场景应该怎么配

读完后,你至少应该能回答三个关键问题:

  1. 为什么 proxy_read_timeout 300s 也不一定能撑住一个 10 分钟请求
  2. 为什么上传大文件时,真正关键的可能不是 proxy_read_timeout
  3. 为什么 timeout 调大,未必是稳定性更高,反而可能更差

1. 先建立一个最重要的心智模型

先说结论:

Nginx 中大多数 timeout,控制的不是“整个请求最多跑多久”,而是“某一次 I/O 操作之间允许空闲多久”。

这句话非常关键。

很多人以为:

proxy_read_timeout 60s;

表示“后端接口必须在 60 秒内全部返回完”。

这并不准确。

它更接近下面这个意思:

Nginx 在向上游读取响应时,如果连续 60 秒都没有读到任何新数据,就判定超时。

也就是说:

  1. 如果后端每隔 30 秒吐一个字节,理论上连接可以一直活着
  2. 如果后端前 59 秒完全没返回,第 60 秒前刚好返回一点数据,计时又会重新开始
  3. 真正决定“请求总耗时”的,往往不是单个 timeout,而是整条链路上多个组件共同作用的结果

所以要理解 Nginx timeout,不能只盯参数名字,而要先看整个链路。

1.1 一个 HTTP 请求会经过哪些等待阶段

假设 Nginx 做反向代理,一个请求通常会经历这些阶段:

  1. 客户端和 Nginx 建立连接
  2. 客户端把请求头发给 Nginx
  3. 客户端把请求体发给 Nginx
  4. Nginx 和上游服务建立连接
  5. Nginx 把请求转发给上游服务
  6. Nginx 等上游服务返回响应
  7. Nginx 把响应发回客户端
  8. 请求结束后,连接可能继续 keepalive,也可能关闭

你会发现,不同 timeout 管的是不同阶段:

  1. client_header_timeout 管第 2 步
  2. client_body_timeout 管第 3 步
  3. proxy_connect_timeout 管第 4 步
  4. proxy_send_timeout 管第 5 步
  5. proxy_read_timeout 管第 6 步
  6. send_timeout 管第 7 步
  7. keepalive_timeout 管第 8 步

这就是理解它们差异的主线。


2. 客户端到 Nginx:请求接收阶段的 timeout

先看客户端这一侧,也就是“浏览器或调用方把请求交给 Nginx”这部分。

2.1 client_header_timeout

client_header_timeout 10s;

它控制的是:Nginx 等待客户端把完整请求头发送完的时间

如果客户端连请求头都迟迟发不完,Nginx 会返回 408 Request Timeout

这个参数主要用来防御两类问题:

  1. 非常慢的客户端
  2. 故意拖住连接、缓慢发送请求头的攻击行为,比如 Slowloris

什么时候要关注它

大多数普通业务里,请求头本来就很小,所以它通常不是性能瓶颈,而是安全和资源保护参数。

比较常见的建议是:

  1. 公网入口不要配得太大
  2. 内网高信任环境可以适当放宽
  3. 如果遭遇慢请求头攻击,要结合连接数限制一起看,而不是只改 timeout

2.2 client_body_timeout

client_body_timeout 30s;

它控制的是:Nginx 在读取客户端请求体时,两次读操作之间允许空闲多久

这个参数最常见于:

  1. 文件上传
  2. 大 JSON / 表单提交
  3. 客户端网络很慢的移动端场景

这里最容易产生误解。

它不是说“文件上传总时长不能超过 30 秒”,而是说:

在上传过程中,如果客户端连续 30 秒都没有继续发送任何请求体数据,Nginx 才判定超时。

所以一个 2GB 文件,只要客户端一直稳定地往前传,即使总耗时远大于 30 秒,也不一定超时。

它和 client_max_body_size 不是一回事

很多人会把这两个配置混在一起:

client_max_body_size 100m;
client_body_timeout 30s;

它们控制的是两个完全不同的问题:

  1. client_max_body_size 限制“能不能上传这么大”
  2. client_body_timeout 限制“上传过程中能不能长时间不发数据”

前者触发通常是 413 Request Entity Too Large,后者触发通常是 408 Request Timeout

2.3 keepalive_timeout

keepalive_timeout 65s;

它控制的是:客户端和 Nginx 之间的 HTTP keepalive 空闲连接,可以保留多久

注意,这不是“单个请求执行多久”,而是“请求已经结束后,这个 TCP 连接还能空闲等下一次请求多久”。

作用非常直接:

  1. 配得大一点,可以减少重复建连成本
  2. 配得太大,会让大量空闲连接占住文件描述符和内存
  3. 配得太小,短连接变多,握手开销上升

公网 Web 服务一般会在“减少建连开销”和“控制空闲连接资源”之间取平衡,通常不会无限放大。

2.4 send_timeout

send_timeout 60s;

它控制的是:Nginx 向客户端发送响应时,两次写操作之间允许空闲多久

这个参数经常被误解成“整个响应必须在 60 秒内发完”,其实也不是。

更准确地说:

如果客户端接收能力太差,导致 Nginx 连续一段时间都无法继续把数据写出去,才会触发 send_timeout

所以它主要针对的是“慢下载客户端”,不是“后端处理太慢”。

典型场景

  1. 客户端网络很差,下载大文件特别慢
  2. 某些恶意客户端故意维持低速读取,占住服务器连接
  3. 大量长时间慢速下行,拖垮 worker 资源

如果你的问题是“后端接口响应太慢”,调 send_timeout 通常没有意义,因为它管的是下行发送,不是上游处理。


3. Nginx 到上游服务:反向代理阶段的 timeout

这部分是大家最常改、也最容易改错的地方。

假设你有这样的配置:

location /api/ {
    proxy_pass http://backend;
}

这时最核心的三个 timeout 是:

  1. proxy_connect_timeout
  2. proxy_send_timeout
  3. proxy_read_timeout

3.1 proxy_connect_timeout

proxy_connect_timeout 3s;

它控制的是:Nginx 与上游建立 TCP 连接最多等多久

这个阶段还没有开始发业务请求,纯粹是在连后端。

如果这里超时,常见表现通常是:

  1. 上游实例挂了
  2. 网络不通
  3. SYN 建连迟迟得不到响应
  4. 上游监听端口异常

这类问题更偏基础设施或网络连通性,通常不是业务代码执行慢。

配置思路

proxy_connect_timeout 通常不建议很大。

原因很简单:

  1. 连不上就是连不上
  2. 如果连接阶段都要等很久,问题往往不在“多给几秒”能解决
  3. 等太久只会让故障扩散得更慢、积压得更多

在很多生产环境里,连接超时常常会配得比读取超时短得多。

3.2 proxy_send_timeout

proxy_send_timeout 30s;

它控制的是:Nginx 向上游发送请求时,两次写操作之间允许空闲多久

注意,它不是“整个请求必须 30 秒内发给上游”,而是发送过程中不能长时间卡住。

这个参数常见于:

  1. 请求体很大,需要转发上传给后端
  2. 上游接收能力弱,socket 缓冲区迟迟消化不掉
  3. 请求流式转发到后端

如果你做的是大文件上传到应用服务器,这个参数就比很多人想象中更重要。

3.3 proxy_read_timeout

proxy_read_timeout 60s;

它控制的是:Nginx 读取上游响应时,两次读操作之间允许空闲多久

这是最常见、也最容易被滥用的参数。

很多人遇到 504 Gateway Timeout,第一反应就是把它调大。这个动作有时有效,但你必须先想清楚:

  1. 上游是真的正常,只是业务确实耗时长
  2. 还是上游卡死、线程池耗尽、数据库锁等待、外部依赖超时

如果是第 2 种,单纯调大只是在把坏请求留得更久。

一个特别重要的细节

proxy_read_timeout 控制的是“读响应期间的空闲间隔”,不是总耗时上限。

举个例子:

  1. 一个 SSE 接口每 15 秒发一条事件
  2. 你配了 proxy_read_timeout 60s

那么这个连接完全可以长时间保持,因为每次事件都会刷新读超时计时器。

反过来:

  1. 一个后端接口需要先计算 70 秒
  2. 这 70 秒里一字节都不返回
  3. 你配了 proxy_read_timeout 60s

那它就会在 60 秒左右超时,即使后端第 61 秒已经快算完了。

这也是为什么“长任务接口”有时应该改成异步任务加轮询,而不是无脑把 proxy_read_timeout 调到几百秒。


4. FastCGI、uWSGI、SCGI:只是协议不同,思路完全一样

如果你的 Nginx 后面不是普通 HTTP 服务,而是 PHP-FPM、uWSGI 或 SCGI,那么参数名会变成:

  1. fastcgi_connect_timeout
  2. fastcgi_send_timeout
  3. fastcgi_read_timeout
  4. uwsgi_connect_timeout
  5. uwsgi_send_timeout
  6. uwsgi_read_timeout
  7. scgi_connect_timeout
  8. scgi_send_timeout
  9. scgi_read_timeout

它们本质上只是把 proxy_* 的语义映射到不同上游协议:

  1. *_connect_timeout 管连接建立
  2. *_send_timeout 管请求发送
  3. *_read_timeout 管响应读取

所以如果你理解了 proxy_* 三件套,这一组参数并没有新的心智负担。


5. DNS 解析:resolver_timeout 是什么

resolver 10.0.0.2 valid=30s;
resolver_timeout 5s;

resolver_timeout 控制的是:Nginx 向 DNS 解析器发起查询后,最多等多久

这个参数常见于:

  1. proxy_pass 使用域名
  2. 上游地址需要动态解析
  3. 容器、Kubernetes、Service Mesh 等环境里依赖内部 DNS

如果 DNS 解析慢或者不稳定,你会看到一种很迷惑的现象:

  1. 后端服务其实活着
  2. 但 Nginx 还是访问失败
  3. 问题根源不是应用,而是解析不到地址

在这种场景里,盯着 proxy_read_timeout 是没用的,因为请求连真正的上游都还没找到。


6. 把这些 timeout 按“方向”和“阶段”记住

如果你总记不住这些名字,可以用下面这个方式强行分类。

6.1 客户端到 Nginx

  1. client_header_timeout:客户端发请求头太慢
  2. client_body_timeout:客户端发请求体太慢
  3. send_timeout:客户端收响应太慢
  4. keepalive_timeout:请求结束后,客户端空闲连接保留多久

6.2 Nginx 到上游

  1. proxy_connect_timeout:连上游太慢
  2. proxy_send_timeout:给上游发请求太慢
  3. proxy_read_timeout:等上游回响应太慢

6.3 名字相同但对象不同

最容易混淆的是下面两组:

  1. send_timeout 是 Nginx 发给客户端
  2. proxy_send_timeout 是 Nginx 发给上游

还有:

  1. client_body_timeout 是 Nginx 收客户端请求体
  2. proxy_read_timeout 是 Nginx 收上游响应体

你只要把 Nginx 想成中间人,这些方向关系一下就清楚了。


7. 常见状态码和日志,通常在暗示什么

timeout 真正难的地方,不是名字,而是线上出问题时该先怀疑谁。

7.1 408 Request Timeout

通常意味着客户端在向 Nginx 发送请求头或请求体时太慢。

优先排查:

  1. client_header_timeout
  2. client_body_timeout
  3. 是否有异常慢客户端或攻击流量

7.2 413 Request Entity Too Large

这不是 timeout,而是体积限制问题。

优先排查:

  1. client_max_body_size
  2. 上传接口是否需要单独放宽

7.3 499 Client Closed Request

这是 Nginx 非常有代表性的一个状态码,表示:客户端先断开了连接

典型场景:

  1. 前端等待太久,用户刷新页面了
  2. 调用方自己的超时比 Nginx 更短
  3. 上游处理太慢,用户提前放弃

这时你不能只盯 Nginx,要把客户端、网关、上游应用的超时一起看。

7.4 502 Bad Gateway

常见于:

  1. proxy_connect_timeout 失败
  2. 上游进程异常退出
  3. 上游协议不匹配
  4. DNS 解析失败

它并不一定意味着“上游处理超时”,也可能是压根没连上。

7.5 504 Gateway Timeout

这是最典型的“上游等待超时”信号。

优先排查:

  1. proxy_read_timeout
  2. 上游线程池、连接池是否耗尽
  3. 数据库慢 SQL、锁等待、下游依赖超时
  4. 是否需要改成异步任务

一个原则:

504 往往只是症状,根因通常在更后面的服务。


8. 几类特别常见的业务场景,应该怎么配

参数本身不难,难的是落到场景。

8.1 普通 Web API

比如后台管理、用户中心、CRUD 接口。

这类接口的特征通常是:

  1. 请求体不大
  2. 响应时间预期比较短
  3. 超时太长反而会掩盖问题

一个常见思路是:

location /api/ {
    proxy_connect_timeout 3s;
    proxy_send_timeout 15s;
    proxy_read_timeout 30s;
    send_timeout 30s;
    proxy_pass http://backend;
}

这种配置背后的思路不是“绝对正确”,而是:

  1. 连接建立要快失败
  2. 正常业务请求不应该无限慢
  3. 出现异常时尽早暴露,而不是把请求挂很久

8.2 文件上传接口

文件上传最容易配错,因为很多人只改 proxy_read_timeout

实际上更关键的通常是:

  1. client_max_body_size
  2. client_body_timeout
  3. proxy_send_timeout

如果上传要先经过 Nginx 再到后端,可以参考这种思路:

location /upload/ {
    client_max_body_size 500m;
    client_body_timeout 120s;
    proxy_connect_timeout 3s;
    proxy_send_timeout 120s;
    proxy_read_timeout 60s;
    proxy_pass http://backend;
}

这里的逻辑是:

  1. 允许较大体积
  2. 允许较慢但持续的上传
  3. 给 Nginx 往上游传请求体足够时间
  4. 上游处理完成后的等待时间另算

8.3 长轮询、SSE、流式输出

这类请求和普通 API 最大的区别是:

  1. 连接会持续很久
  2. 但只要周期性有数据流动,未必算超时

比如 SSE:

location /events/ {
    proxy_connect_timeout 3s;
    proxy_read_timeout 1h;
    proxy_send_timeout 1h;
    proxy_buffering off;
    proxy_pass http://backend;
}

这里重点不是单纯把 timeout 调大,而是你要明确:

  1. 上游是否会定期发送心跳
  2. 中间链路是否还有其他更短的超时
  3. 是否需要关闭缓冲,避免流式响应被攒住

如果后端 10 分钟完全不发任何字节,那么就算你以为它在“流式处理”,实际上对 proxy_read_timeout 来说,依然只是长时间沉默。

8.4 WebSocket

WebSocket 升级成功后,本质上是长连接。

比较常见的思路是:

location /ws/ {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 1h;
    proxy_send_timeout 1h;
    proxy_pass http://backend;
}

这里你真正要关心的是:

  1. 应用层是否有 ping/pong 或心跳
  2. 中间网关、LB、云厂商连接超时是多少
  3. 断连重连策略是否健全

因为很多时候,断开连接的并不是 Nginx,而是更前面一层的负载均衡。

8.5 大文件下载

如果你的用户要通过 Nginx 下载很大的文件,重点通常不在 proxy_read_timeout,而在:

  1. send_timeout
  2. 是否支持断点续传
  3. 是否由 Nginx 直接回源静态文件
  4. 客户端网络质量是否足够稳定

如果客户端接收很慢,Nginx 迟迟发不出去,send_timeout 就可能触发。


9. timeout 不要单看 Nginx,要看整条调用链

线上最常见的误区是:

既然报错发生在 Nginx,就只改 Nginx。

实际上一条请求可能同时经过:

  1. 浏览器或 SDK 自己的超时
  2. CDN / WAF / 云负载均衡超时
  3. Nginx 超时
  4. 应用服务器超时
  5. 应用里的 HTTP client 超时
  6. 数据库、缓存、消息队列、第三方 API 超时

这些超时如果配置得前后矛盾,就会出现很难排查的问题。

9.1 一个常见反模式

  1. 前端 30 秒超时
  2. Nginx proxy_read_timeout 300s
  3. 应用内部调用数据库超时 600s

结果是:

  1. 用户 30 秒就走了
  2. Nginx 还在等
  3. 后端还在查数据库
  4. 整条链路白白占资源

这类配置非常低效。

更合理的思路通常是:

  1. 最外层超时要和用户体验预期一致
  2. 越往内层,超时通常应略短于外层,而不是更长很多
  3. 长任务不要伪装成同步短请求

9.2 一个可操作的经验原则

虽然没有绝对公式,但很多系统会遵循类似策略:

  1. 调用方超时略短于网关可等待时间
  2. 网关超时略短于上游应用可容忍时间
  3. 上游应用访问下游依赖时,超时再更短一点

这样做的目标是让失败尽早在更靠里的地方被感知和释放,而不是把压力一层层往外拖。


10. 为什么 timeout 不能无脑调大

把 timeout 调大,短期看似能“减少报错”,但代价经常被低估。

10.1 连接会被占更久

每一个长时间挂着的请求,都可能占住:

  1. socket
  2. 文件描述符
  3. worker 处理资源
  4. upstream 连接池位置
  5. 应用线程或协程

如果慢请求一多,系统吞吐量会明显下降。

10.2 故障传播更慢,但更深

如果后端已经雪崩,短 timeout 会让问题快速暴露;长 timeout 则可能让更多请求堆进去,直到把更多组件一起拖垮。

这和“快速失败”是同一个工程原则。

10.3 用户体验不一定更好

用户等待 10 秒和等待 120 秒,很多时候感受上不是线性变差,而是前者还能接受,后者已经放弃。

所以 timeout 的本质不是“越大越稳”,而是:

用合理的等待上限,保护系统和用户体验。


11. 一套更实用的排障顺序

线上遇到 timeout,不建议上来就改配置。更稳妥的顺序通常是:

  1. 先确认是客户端到 Nginx 慢,还是 Nginx 到上游慢
  2. 看状态码是 408499502 还是 504
  3. 结合 access log 和 error log 判断卡在哪个阶段
  4. 确认是不是 DNS、建连、发送、读取这四类中的哪一类
  5. 再决定是调 timeout,还是修后端性能、网络、连接池、SQL、线程池

11.1 你至少要回答这几个问题

  1. 请求有没有到达上游应用
  2. 上游是根本没收到,还是收到了但处理很慢
  3. 是首字节迟迟不来,还是中途流断了
  4. 客户端有没有提前取消
  5. 整条链路上是否还有别的网关比 Nginx 更早超时

如果这些问题没答清楚,直接改 timeout,大概率只是碰运气。


12. 给一份相对稳妥的理解框架

如果你只想记住最核心的东西,可以记这一版:

  1. client_*_timeout 管客户端把请求交给 Nginx
  2. proxy_*_timeout 管 Nginx 把请求交给上游并等待响应
  3. send_timeout 管 Nginx 把响应发回客户端
  4. keepalive_timeout 管请求结束后连接还能闲置多久
  5. 大多数 timeout 管的是“空闲间隔”,不是“整个请求总时长”
  6. 504 往往不是把 timeout 调大就能真正解决,根因通常在后端

把这六点真正理解透,Nginx 里大部分 timeout 配置就已经不会再混乱了。


13. 结语

Nginx 的 timeout 看起来只是一些普通配置项,但它背后其实是在回答一个很工程化的问题:

一个系统愿意为哪一类等待付出多大代价。

等待客户端、等待上游、等待 DNS、等待下一次复用连接,这些等待都不是免费的。

所以 timeout 的设计,本质上是在做平衡:

  1. 平衡用户体验和系统保护
  2. 平衡吞吐和容错
  3. 平衡“不要误杀正常慢请求”和“不要放任异常请求长期占资源”

如果你以后再看到一条超时配置,不要先问“该改多大”,而是先问:

  1. 这是哪一段链路的等待
  2. 它限制的是总时长,还是 I/O 空闲间隔
  3. 如果这个阶段长期变慢,真正的根因会在哪里

把这三个问题想清楚,你配 timeout 的准确率会高很多。