欢迎光临
我们一直在努力

413和404

一:问题

开发过程中遇到一个比较有意思的问题,记录一下完整的排查过程。

系统架构比较简单:

前端

Nginx :9999

Spring Boot :8080

上传文件时,接口出现了一个比较奇怪的现象:

第一次请求返回 413 Request Entity Too Large,紧接着再次请求却变成了 404 Not Found。

而且接口路径本身是正确的,正常情况下这个接口一直都可以访问。

问题就变成了:

为什么同一个接口,第一次是 413,后面又变成了 404?


二:排查

1. 先定位 413 到底是谁返回的

首先想到的是文件上传大小限制。

HTTP 413 Request Entity Too Large(现在更常见的描述是 413 Content Too Large)通常意味着:

请求体超过了服务端允许的最大大小。

整个请求链路中可能存在多个限制:

客户端

Nginx

Tomcat

Spring Boot

Controller

因此第一步就是:

绕过 Nginx,直接访问后端 8080。

例如:

curl http://127.0.0.1:8080/api/xxx

上传同一个文件进行测试。

测试结果:

直接访问 8080:正常
访问 Nginx 9999:异常

这时候基本可以确定:

问题发生在 Nginx 这一层,而不是 Spring Boot Controller。

如果直连 8080 正常,而经过 9999 就返回 413,那么重点就应该检查 Nginx 的请求体大小限制。

2. 检查 Nginx 的 client_max_body_size

查看 Nginx 配置:

nginx -T

或者直接搜索:

nginx -T | grep -n client_max_body_size

发现原来的 Nginx 配置中没有设置:

client_max_body_size

Nginx 默认的请求体大小限制通常是:

1 MB

而当前接口需要上传较大的文件,因此超过限制后,Nginx 直接返回:

413 Request Entity Too Large

请求甚至不会到达 Spring Boot。

解决方法

可以针对上传接口所在的 location 配置:

location ^~ /api/ {
client_max_body_size 200M;

proxy_pass http://127.0.0.1:8080/api/;

proxy_redirect default;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Authorization $http_authorization;
}

然后检查配置:

nginx -t

确认:

syntax is ok
test is successful

再重新加载:

nginx -s reload

3.为什么又出现 404?

按照正常逻辑,修改 client_max_body_size 后,413 应该解决。

但是当时还遇到了另外一个现象:

第一次请求出现 413,之后再次请求却出现 404。

而且这个 404 返回的页面也是 Nginx 默认的:

<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<center><h1>404 Not Found</h1></center>
<hr>
<center>nginx/1.27.4</center>
</body>
</html>

这说明至少有一个请求没有正常进入后端 Controller,而是在 Nginx 层就结束了。

于是开始进一步排查。


4.排查 HTTP 长连接和连接复用

一个值得验证的方向是:

是否和 HTTP Keep-Alive / TCP 连接复用有关?

因为浏览器、Axios、curl 等 HTTP 客户端通常都会复用 TCP 连接。

为了验证这一点,可以让客户端每次请求都关闭连接。

例如使用 curl:

curl -H "Connection: close" …

如果使用 Axios,也可以临时关闭 Agent 连接复用,例如:

axios({
url: '/api/xxx',
method: 'post',
data: formData,
// 根据具体运行环境配置 Agent
});

Node.js 环境下可以进一步检查 HTTP Agent 的连接复用情况。

这个测试的目的不是直接证明“连接被污染”,而是:

通过关闭连接复用,观察 413 → 404 的现象是否消失。

如果关闭 Keep-Alive 后问题明显变化,就说明连接复用值得重点调查。


5.发现一个值得关注的 Nginx 配置

继续检查 Nginx 配置时,发现了这样一行:

keepalive_timeout 60000s;

这个配置非常夸张。

60000 秒 ≈ 16.7 小时

也就是说,Nginx 可以让 HTTP Keep-Alive 连接保持非常长的时间。

通常没有特殊需求的话,并不需要把这个值设置得这么大。

例如:

keepalive_timeout 65s;

或者根据实际业务场景配置一个合理的值。

因为 keepalive_timeout 高达 16 小时,一条被 413 中断、body 没读干净的连接会长时间滞留在连接池里被反复复用,残留字节被当成新请求解析 → 404。等这条连接最终超时/被关闭后,再试又恢复正常。

四:思路

这次问题可以总结成下面的排查链路:

上传大文件


经过 Nginx :9999

├── 请求体超过 Nginx 限制


413 Request Entity Too Large


检查 client_max_body_size


发现没有配置


Nginx 默认限制约 1MB


增加 client_max_body_size

例如:

location ^~ /api/ {
client_max_body_size 200M;

proxy_pass http://127.0.0.1:8080/api/;
}

同时检查:

keepalive_timeout 65s;

不要无特殊需求设置成:

keepalive_timeout 60000s;

五:本质

一句话概括:"大文件 + 连接复用"问题

  • 413 (Payload Too Large):请求体超过了某一层的大小限制,请求根本没进入你的 Controller(imageUpload 方法都没执行)。
  • 404 (Not Found):服务端把收到的字节当成了一个"不存在的路径"。

它们交替出现,是典型的 HTTP keep-alive 长连接被"污染" 导致的:

核心机制:当上传文件超过限制时,服务端(nginx 或 Tomcat)直接返回 413 并中断,但没有把请求体读完/吞掉。客户端的连接池认为这条 keep-alive 连接还能用,于是复用它发下一个请求;此时上一个请求残留的 multipart 二进制字节还留在 TCP 流里,被服务端当成新请求的"请求行"去解析,路径自然对不上 → 返回 404。等这条脏连接被彻底关闭后,再试又恢复正常。这就是你说的"有时 413,再调用又变 404"

赞(0)
未经允许不得转载:171主机测评 » 413和404
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址