news 2026/10/5 10:47:10

Linux Nginx proxy_send_timeout 参数怎么配置优化大文件上传

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux Nginx proxy_send_timeout 参数怎么配置优化大文件上传

前言

先纠正这个标题里的前提:proxy_send_timeout并不是"上传超时",把它调大通常也解决不了大文件上传失败。官方文档对它的定义是"Nginx 向被代理服务器(上游)发送请求时的超时,且只在两次相邻写操作之间计时,不是整个请求的传输时长"。方向是 Nginx 到后端,不是浏览器到 Nginx。

更关键的是默认行为:proxy_request_buffering的默认值是on,意思是Nginx 会先把客户端的请求体完整收下来(超过内存缓冲的部分落到临时文件),收完之后才向后端发起请求、把 body 转发过去。所以在默认配置下,浏览器上传大文件的过程根本没有触碰到proxy_send_timeout——真正决定成败的是客户端到 Nginx 这一段的那几个参数。

这就解释了一类很常见的现象:上传大文件失败,把proxy_send_timeout从 60s 调到 600s,一点用都没有;而把client_max_body_size从默认的1m改大,问题立刻消失。

proxy_send_timeout真正开始起作用,是在你把proxy_request_buffering off打开、让 Nginx 边收边转发之后——这时候 Nginx 向上游的写入节奏完全由客户端上传速度决定,proxy_send_timeout才成为"上传速度"的实际约束。本文把这条完整链路拆开讲清楚:哪一段归哪个指令管、怎么定位日志、以及两种工作模式下分别该怎么配。示例基于 nginx 1.24 / 1.22。

一、先分清四个超时各自管哪一段

指令默认值计量方式管的是
client_header_timeout60s读请求头整体客户端发请求头太慢
client_body_timeout60s两次相邻读操作之间客户端上传 body 时的停顿
send_timeout60s两次相邻写操作之间Nginx 把响应写回客户端时的停顿
proxy_connect_timeout60s建连Nginx 连不上上游
proxy_send_timeout60s两次相邻写操作之间Nginx 把请求发给上游时的停顿
proxy_read_timeout60s两次相邻读操作之间上游迟迟不返回
keepalive_timeout75s空闲长连接空闲回收

这张表里最重要的是"计量方式"这一列。这几个超时的语义都是相邻两次 I/O 之间的间隔,而不是"整个请求从开始到结束最长多久"。所以一个逻辑上"传了 30 分钟"的大文件,只要数据一直在流动(每两次写之间没有超过阈值),就不会触发超时;反过来,一个只传了 3 秒的连接,如果中间卡了 61 秒没动静,照样会被掐掉。

二、大文件上传的完整链路:每个阶段谁在管

按数据流动的顺序走一遍,把每个阶段的控制点标出来:

阶段控制指令默认值典型症状
客户端发请求头client_header_timeout60s少见的头部读取超时
请求体超过上限client_max_body_size1m413 Request Entity Too Large
请求体在内存里还是落盘client_body_buffer_size`8k\16k`
临时文件放哪client_body_temp_path默认在编译前缀下的client_body_temp目录满 → 500
客户端上传时的停顿client_body_timeout60swhile reading client request body
Nginx 向上游发 bodyproxy_send_timeout60s只在proxy_request_buffering off时才是主要约束
上游处理后返回proxy_read_timeout60swhile reading response header from upstream
Nginx 把响应写回客户端send_timeout60s客户端下载响应太慢被断开

第一件该做的事就是把client_max_body_size调到你需要的量级。默认值1m意味着任何超过 1MB 的上传都会在请求头解析完后立刻被拒,客户端拿到 413,连数据都没开始传:

# 允许 2GB 的请求体;设为 0 表示不检查(即不限制) client_max_body_size 2g;

这条指令可以放在http、server、location里,按站点或按路径精细控制比全局放开更合适。

三、两种工作模式:缓冲与流式

模式 A:默认的缓冲模式

Nginx 先把整个请求体收完(内存缓冲不够就落临时文件),再转发给上游。它的好处是上游不必处理慢速上传、请求可以被重试和均衡;代价是磁盘要能承下所有并发上传的体积之和,且上游要等到全部收完才开始处理。

location /upload/ { proxy_pass http://127.0.0.1:8080; client_max_body_size 2g; # 默认 1m,必须调 client_body_timeout 300s; # 客户端慢速上传的停顿容忍 client_body_buffer_size 1m; # 超过此值写入临时文件 proxy_send_timeout 300s; # 收完之后发给上游时的停顿容忍 proxy_read_timeout 300s; # 上游处理大文件可能需要更久 # 临时目录:不写则使用编译时的默认值。写的话目录必须存在且属主正确 client_body_temp_path /var/lib/nginx/body 1 2; }

client_body_temp_path后面可以跟最多三层子目录参数(如上面的1 2),作用是把大量临时文件分散到多级子目录里,避免单目录海量文件导致性能下降。这个目录必须已经存在,并且属主是该 worker 进程运行的用户:RHEL 系通常是nginx,Debian 系是www-data。目录不存在时 Nginx 不会自动创建,上传会以 500 失败。

模式 B:关闭请求缓冲,边收边转发

location /upload/ { proxy_pass http://127.0.0.1:8080; proxy_request_buffering off; # 1.7.11 起可用,默认是 on proxy_http_version 1.1; # 需要流式/分块传输时要显式指定 client_max_body_size 2g; client_body_timeout 300s; proxy_send_timeout 300s; # 这个模式下它才真正管住上传速度 proxy_read_timeout 300s; }

这个模式才有本文标题所说的意义:Nginx 一边从客户端读,一边往上游写,客户端的上传速度直接决定了写入间隔。上传慢的客户端会让proxy_send_timeout变得敏感,这时把它调大是合理的。

但要注意三个真实代价:


  1. 上游必须能处理流式请求。Nginx 可能无法预先知道请求体长度,会以分块传输(chunked)方式发给上游,因此通常需要proxy_http_version 1.1,并且上游得支持分块编码。

  2. 请求不能再被重试或转投另一台上游。数据已经开始发出去,中途失败就没法悄悄重发。用proxy_next_upstream的常规策略在这里帮不上忙。

  3. 上游会提前看到"慢客户端"。应用可能因为读不到完整数据而超时,这是把缓冲挪走之后的必然代价,需要在应用侧相应调整。


选择标准很直白:希望降低 Nginx 的磁盘占用和首字节延迟,用模式 B;希望上游逻辑简单、能重试、磁盘足够,用模式 A。

四、定位:从日志判断卡在哪一段

上传失败时,error.log里的一句话通常就能定位到具体指令。下面是几类常见记录的对照(不同 Nginx 版本的措辞略有差异,以你机器上的实际输出为准):

grep -E 'too large body|timed out|no space|prematurely' /var/log/nginx/error.log | tail -30
error.log 片段对应指令
client intended to send too large body: 3145728 bytesclient_max_body_size
client timed out (110: Connection timed out) while reading client request bodyclient_body_timeout
upstream timed out (110: Connection timed out) while sending request to upstreamproxy_send_timeout
upstream timed out (110: Connection timed out) while reading response header from upstreamproxy_read_timeout
upstream prematurely closed connection while sending request to upstream上游自己断了(可能是它自己的限制)
(28: No space left on device) while reading client request body临时目录所在分区写满

顺手确认最终生效的值,避免改了没生效:

nginx -T 2>/dev/null | grep -n -E 'client_max_body_size|client_body_timeout|client_body_temp_path|proxy_(send|read)_timeout|proxy_request_buffering'

再看临时目录的余量,以及配置块之外的限制也别忘了:

# 找到实际使用的临时目录并看剩余空间 df -h /var/lib/nginx # SELinux enforcing 时目录标签不对也会失败 sudo ls -Zd /var/lib/nginx/body sudo restorecon -Rv /var/lib/nginx/body

五、动手复现和验证

用一个大文件走一遍完整流程,同时观察各阶段的耗时。

# 1) 造一个 200MB 的测试文件(GNU coreutils 的 dd 支持 status=progress) dd if=/dev/zero of=/tmp/blob.bin bs=1M count=200 status=progress # 2) 用表单方式上传,并把各阶段时间与上传速率打出来 curl -s -o /dev/null \ -w 'connect=%{time_connect} starttransfer=%{time_starttransfer} total=%{time_total} speed_upload=%{speed_upload}B/s http=%{http_code}\n' \ -F 'file=@/tmp/blob.bin' https://upload.example.com/upload/

%{speed_upload}是 curl 报的实际上传速率,%{http_code}能直接告诉你是不是 413。把这两组数字在调整参数前后各测一次,收益是你自己测出来的,不需要参考任何人的经验值。

想验证proxy_send_timeout到底怎么触发,可以在测试环境里扮演一个"只收不答、收得极慢"的上游:

# OpenBSD 版 netcat 的写法 nc -l 8080 > /dev/null # 部分传统版 netcat 需要写成 # nc -l -p 8080 > /dev/null

让proxy_pass指向这个端口,再上传一个大文件,就能在error.log里看到while sending request to upstream的字样——用它来确认你的参数确实作用在这一段上,比读文档印象深得多。

调试阶段还有一个很有用的指令,它把请求体无条件写进文件:

client_body_in_file_only on; # 取值 on | clean | off,默认 off;仅用于排障

用它确认"请求体有没有完整到达 Nginx",可以把"客户端没传完"和"上游没收到"两种情况区分开。用完记得改回off。

六、调参的原则:成组调,按最慢的合法用户定


  • 这几个超时是成组生效的,只调一个往往没意义。至少要同时考虑client_body_timeout、proxy_send_timeout、proxy_read_timeout、send_timeout。

  • 取值依据应该是"最慢的合法用户":用第五节的方法测出真实上传耗时,再留 2 到 3 倍余量,而不是随手写个3600s。

  • 超时不是限速。想限速用limit_rate(对响应)或limit_conn/应用层限速。把client_body_timeout设小来"限速"只会让正常用户传大文件失败。

  • 调大超时等于允许连接占住 worker 更久,连接数会上升,记得同步检查worker_connections与worker_rlimit_nofile。

  • 临时目录所在分区的容量要按"并发上传数 × 单文件大小"估算。这条最容易被忽略,而它一旦满了,症状是 500 而不是超时,容易排查到别的方向去。


常见坑点

❌ 上传大文件失败,第一反应是把proxy_send_timeout调到 600s。 ✅ 默认proxy_request_buffering on时,这个超时管的是 Nginx 收完 body 之后发往上游的过程。先看是不是 413,也就是client_max_body_size(默认仅1m)。

❌ 把proxy_send_timeout理解成"整个请求最长可以传多久"。 ✅ 它的语义是相邻两次写操作之间的最大间隔,且只作用于 Nginx 到上游这一段。整个请求传得久没关系,中间长时间没有数据流动才会被断开。

❌ 只调client_body_timeout,忘了client_max_body_size。 ✅ 前者管"停顿"(默认 60s),后者管"总体积"(默认 1m)。体积超限是立刻返回 413,跟超时无关。

❌ 用超时参数来给上传限速。 ✅ 这是拿错误工具干正确的事。限速用limit_rate/limit_conn,超时只该用来兜异常。

❌ 开了proxy_request_buffering off却没有proxy_http_version 1.1。 ✅ 流式转发需要分块传输支持,上游通常也需要 HTTP/1.1 才能正确接收不定长的请求体。

❌ 开了proxy_request_buffering off,却指望失败时能自动重试到另一台上游。 ✅ 请求体已经开始发送,无法重放。这个模式的代价之一就是失去重试能力,需要应用层做补偿。

❌ 改了client_body_temp_path指向一个新目录,没建目录也没改属主。 ✅ 目录必须事先存在,属主是 worker 运行用户(RHEL 系nginx,Debian 系www-data),SELinux 下还需要正确的上下文(restorecon)。否则上传会以 500 失败,错误信息跟超时完全不像。

❌ 直接在http块把client_max_body_size设成0(不限制),图省事。 ✅ 这在暴露到公网的站点上等于取消了一道防护。按业务路径单独放开,并在边缘设备上做体积与速率限制。

总结

症状 / 目标先看哪条指令默认值备注
413 Request Entity Too Largeclient_max_body_size1m大文件上传第一嫌疑人
上传到一半断,日志while reading client request bodyclient_body_timeout60s相邻读操作之间的停顿
日志while sending request to upstreamproxy_send_timeout60s只在关掉请求缓冲后才是主要约束
后端处理慢导致 502/504proxy_read_timeout60s相邻读操作之间的停顿
500,日志No space left on deviceclient_body_temp_path所在分区—临时目录容量与权限
希望降低磁盘占用、更快让上游开始处理proxy_request_buffering offon代价是不能重试、上游要支持分块
参数调大的理由代价
client_max_body_size允许大文件攻击面变大,按路径限制
client_body_timeout容忍慢速客户端连接占用更久
proxy_send_timeout流式模式下容忍慢速上传上游连接占用更久
proxy_read_timeout上游处理大文件慢worker 被占住更久

结论:上传大文件失败时,proxy_send_timeout通常不是答案。先确认client_max_body_size、再看client_body_timeout、然后检查临时目录,最后才轮到代理侧的超时;而且只有在主动关掉proxy_request_buffering之后,proxy_send_timeout才真正开始管住上传速度。搞清楚每个超时管的是哪两个操作之间的间隔,这类问题就不会再靠"全都调大试试"来解决了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 10:46:51

AI Agent中的Skill:与Prompt、Tool的区别及实践指南

最近几个月,不管是在技术社区的讨论串里,还是在各种 AI 相关的群里,总能看到有人在晒自己的 "Skill"。有人把 Skill 传得到处都是,有人靠整理别人的 Skill 攒了一波关注,还有人一脸懵地问:这东西…

作者头像 李华
网站建设 2026/10/5 10:46:51

json-server零代码后端模拟:从RESTful API到项目实战

做前端开发这几年,我最怕听到的一句话不是“这个需求下周一上线”,而是“后端接口下周才给,你先看文档把页面写了”。文档里往往只有十几个字段名,返回结构写得模棱两可,等后端真正联调时才发现字段大小写对不上、嵌套…

作者头像 李华
网站建设 2026/10/5 10:46:04

动态标签与SOP触发引擎:让用户运营自动化的实战指南

我见过太多项目死在“标签系统做完了,运营却还在用手工筛人”这一步。原因很简单:多数标签是静态的、靠人肉维护、更新靠周报,等运营看到用户已经“高意向”时,用户大概率已经流失到竞品那边了。用户行为、动态标签、SOP触发引擎这…

作者头像 李华
网站建设 2026/10/5 10:45:14

网络规划设计方案书撰写指南:IP/VLAN规划与评审避坑要点

简介:一份面向医院信息化建设与网络规划学习者的PDF方案书,以某三级甲等医院为实例,系统讲解网络规划设计的完整流程。内容覆盖核心层、分布层、接入层的分层设计原则,网络拓扑结构设计,路由器、交换机与服务器选型&am…

作者头像 李华
网站建设 2026/10/5 10:44:56

VOC疲劳驾驶数据集转YOLO格式:目标检测训练避坑实战指南

简介:疲劳驾驶检测数据集以Pascal VOC格式组织,收录4362张驾驶状态图片及对应XML标注文件,面向计算机视觉方向的算法工程师与研究人员,可直接用于疲劳驾驶检测、驾驶员状态分析等模型的训练与评估。标注工作基于labelImg完成&…

作者头像 李华
网站建设 2026/10/5 10:44:49

ClickHouse实时洞察全链路实践:从Flink CDC同步到数据大屏

做实时洞察这几年,手头数据量一上来,最先撑不住的就是传统的关系型数据库。你明明只是想把当天的订单按小时聚合一下,MySQL 一个 group by 就能把 CPU 打满,跑了一分多钟才出结果,业务方早就不耐烦了。后来我在项目里引…

作者头像 李华