Nginx 413 排错:client_max_body_size 加在哪一层才生效
现象
上传一个 20 多 MB 的附件,前端直接弹 413:
413 Request Entity Too Large
nginx/1.20.2
不是超时,也不是网络中断,请求压根没进后端。
环境
- 链路:浏览器 → Nginx(443) → 反向代理 → Spring Boot(8080)
- 站点配置在
/etc/nginx/conf.d/app.conf - 后端
spring.servlet.multipart.max-request-size早就调到 100MB 了
后端限制已经放开,所以问题不在 Java 这边。
先确认是 Nginx 拦的
这一步别跳。判断方法很简单:去看后端访问日志。
413 是 Nginx 读完请求头、发现 Content-Length 超限就直接返回的,请求不会转发给 upstream。所以如果这次上传在 Spring Boot 的 access log 里完全没有记录,而错误页又是标准的 Nginx 样式、响应头带 Server: nginx,那就基本确定拦在 Nginx 层了。
Nginx 自己的 error.log 里会留一句:
client intended to send too large body: 25165824 bytes
看到这句就不用再怀疑了。
另外,如果这个请求是 JSON 而不是文件,client_max_body_size 一样管,它不是只针对 multipart 上传的。
加在哪
client_max_body_size 只能写在三个地方:http、server、location。作用域越小优先级越高,也就是 location > server > http。
想让整个站点生效,写在 server 块的大括号里面 ,和 listen、server_name 同级,别写进 location:
nginx
server {
listen 443 ssl;
server_name example.com;
client_max_body_size 100m;
root /www/wwwroot/example;
index index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
}
写在 server 块里的哪一行其实无所谓,Nginx 不靠顺序解析这个指令。我习惯塞在 server_name 下面,改的时候一眼能找到。
几个容易写错的位置:
- 写进
http {}外面或者events {}里 ------nginx -t直接报错,指令不允许出现在那儿 - 写进某个
location里 ------ 只对匹配这个 location 的请求生效,其他路径还是默认的 1m - 同一个 server 块里写了两次 ------ 报
"client_max_body_size" directive is duplicate,改之前先 grep 一下有没有旧值
只想放开上传接口
那就别在 server 层放大,收窄到具体路径更安全:
nginx
server {
listen 443 ssl;
server_name example.com;
location /api/upload {
client_max_body_size 100m;
proxy_pass http://127.0.0.1:8080;
}
}
全站放开 100m 意味着任何一个接口都能被打 100MB 的包,能收窄就收窄。
所有站点统一放开
写在 nginx.conf 的 http 块里:
nginx
http {
client_max_body_size 100m;
include /etc/nginx/conf.d/*.conf;
}
之后某个站点想单独收紧,在它的 server 块里再写一次小的值就行。
改完别忘了这一步
bash
nginx -t
nginx -s reload
nginx -t 一定要跑,配置写错位置它会直接告诉你哪一行。
还是 413
reload 之后仍然 413,按这个顺序查:
- 改的不是那台 Nginx。 链路里可能还有一层:CDN、WAF、SLB,或者前面还架着一台反代。看错误页样式和
Server响应头,是谁返回的就改谁。这是最常见的原因。 - 80 和 443 两个 server 块。 只改了监听 80 的那个,实际流量走的是 443 的块,等于没改。
- 后端自己的限制。 Nginx 放行之后请求才轮到应用,PHP 的
post_max_size、upload_max_filesize,Tomcat 的maxPostSize,Express 的express.json({ limit }),各自还得对上。 - 面板改错地方。 用宝塔的话在「网站 → 设置 → 配置文件」里改,改完在面板里重载,别直接手改
/www/server/nginx/conf/nginx.conf就以为生效了。
有没有必要把限制调得很大
不建议。真要传大文件,比堆 client_max_body_size 靠谱的做法是:
- 前端先压缩,或者分片上传 + 断点续传
- 走对象存储预签名 URL 直传,body 根本不过自己的 Nginx
- 流式处理,别把整个 body 一次性读进内存
这个阀门存在的意义就是挡恶意大包,图省事设成 0(等于关掉检查),迟早要在别的地方还回来。
附:各层对应的参数
排查的时候对着找就行:
| 层级 | 配置 |
|---|---|
| Nginx | client_max_body_size 100m; |
| Apache | LimitRequestBody 104857600(单位字节) |
| PHP | upload_max_filesize = 100M、post_max_size = 100M(后者要大于前者) |
| Spring Boot | spring.servlet.multipart.max-file-size、max-request-size |
| Tomcat | maxPostSize、maxSwallowSize |
| Node/Express | express.json({ limit: '100mb' })、multer 的 limits.fileSize |
| 云网关/CDN | API Gateway、Cloudflare、WAF、ALB 各有各的请求体上限 |