bigywater.tech
微服务 · 28

请求体大小限制与 413

请求体超限时 nginx 在什么时候回 413?client 还会不会把整个请求体发完?chunked 上传和流式转发会怎样?怎么按路径放宽?这篇用原始 socket 把几种情形量出来。

200.0 MB
Content-Length 为 200 MB、限制 1m 时,client 实际发出的请求体大小;上游一次都没收到,client 看到 413
1.05 MB
chunked 上传 5 MB、proxy_request_buffering off 时,超限被拒之前上游已经收到的请求体(残缺)
0.0 MB
带 Expect: 100-continue 的 client 在 5 MB 超限时实际发出的请求体(不带的发了 5.0 MB)

Content-Length 超限

实测 · 本机执行输出
  Content-Length | client 看到 | client 实际发出 | 耗时     | 上游收到的请求体
  ---------------+-----------+-------------+--------+---------
  0.5 MB         | 200       | 0.5 MB      | 0.00 s | 0.50 MB 
  2 MB           | 413       | 2.0 MB      | 0.00 s | 没到上游    
  20 MB          | 413       | 20.0 MB     | 0.01 s | 没到上游    
  200 MB         | 413       | 200.0 MB    | 0.08 s | 没到上游    
  Content-Length 超限的请求,nginx 回 413 之后会“延迟关闭”(lingering close):继续把 client 还在发的请求体读掉丢弃一段时间,所以 client 大多还能收到 413;client 发得快、请求体特别大时,发送可能先于 413 的到达而失败(看 client 实际发出的字节数)

读请求体之前就回 413,但 client 仍会把请求体发完,nginx 延迟关闭并丢弃。

chunked 与缓冲

实测 · 本机执行输出
  配置                             | client 看到 | client 实际发出 | 上游收到的请求体
  -------------------------------+-----------+-------------+---------
  proxy_request_buffering on(默认) | 413       | 5.0 MB      | 没到上游    
  proxy_request_buffering off    | 413       | 5.0 MB      | 1.05 MB 
  chunked 没有事先声明长度,nginx 只能边读边数,读超过限制才知道超限:缓冲打开时请求体还没转给上游,上游完全不知情;关闭缓冲(边收边转)时,上游已经收到了一部分请求体,然后连接被掐断,上游看到的是残缺的请求

chunked 只能边读边数;关掉请求缓冲时,上游会收到一部分残缺的请求体。

按路径放宽与 Expect

实测 · 本机执行输出
  请求体大小  | /api(1m) | /upload(50m)
  -------+----------+-------------
  0.5 MB | 200      | 200         
  5 MB   | 413      | 200         
  60 MB  | 413      | 413         
  client                 | 看到  | client 实际发出的请求体 | 耗时    
  -----------------------+-----+-----------------+-------
  不带 Expect              | 413 | 5.0 MB          | 0.00 s
  带 Expect: 100-continue | 413 | 0.0 MB          | 0.00 s

location 级的 client_max_body_size 可以覆盖;Expect: 100-continue 让 client 在超限时省下请求体。

分步演示

请求体大小限制与 413
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。nginx 是 Homebrew 的 1.31.6;上游是我写的原始 TCP 服务(数请求体字节,支持 Content-Length 和 chunked);client 是原始 socket(发头、发请求体、读响应);“client 发出的字节数”是 client 自己的 sendall 累计,不等于 nginx 收到的字节数;每个场景只测一次
    • 没有验证:lingering_close / lingering_time 的参数对 client 能否收到 413 的影响;HTTP/2 上的请求体限制;client_body_buffer_size 与临时文件;上游自己的请求体限制(例如 Tomcat、Spring)与网关限制不一致的情形;自定义 413 的 error_page
    京ICP备2026067502号-1京公网安备11010502063487号