适用场景
业务通过 Nginx 上传图片、安装包、备份文件或导入数据时,小文件正常,大文件刚提交便收到 413 Request Entity Too Large。应用日志没有请求记录,客户端通常只看到一个 Nginx 错误页。
本文以“上传 80 MB 压缩包失败、10 MB 图片正常”为例,说明怎样确认拦截层、放开正确范围,并避免把上传接口变成磁盘与带宽攻击入口。
先理解:413 是谁返回的
HTTP 413 表示请求体超过接收方允许的大小。它不一定由应用返回:在常见的反向代理架构中,Nginx 会先读取请求头,并依据 Content-Length 或读取到的请求体执行大小检查。若检查失败,请求不会转发给 upstream,因此应用访问日志、APM 和业务异常都可能毫无痕迹。
上传链路可能有多层限制:
- CDN、WAF 或负载均衡的请求体限制;
- 最外层 Nginx 的
client_max_body_size; - 内层网关或 Ingress 的同类配置;
- 应用框架、对象存储 SDK 或业务规则的文件大小限制。
不要看到 413 就只改应用配置。先确认响应来自哪一层,才能避免改完仍然失败。
现场排查步骤
1. 用可控大小的文件复现
在测试环境或已授权目录创建不同大小的文件,并记录临界值:
truncate -s 20M upload-20m.bin
truncate -s 80M upload-80m.bin
curl -i -F "file=@upload-20m.bin" https://upload.example.com/api/files
curl -i -F "file=@upload-80m.bin" https://upload.example.com/api/files
-i 会输出响应头。注意 Server、Via、X-Request-ID 等字段:若 Server: nginx 且应用没有对应请求日志,优先检查 Nginx 或其前面的代理。生产环境应使用脱敏测试文件,并限制测试次数,避免占满带宽。
2. 查看 Nginx 错误日志和生效配置
sudo tail -n 100 /var/log/nginx/error.log
sudo nginx -T | grep -n -E 'client_max_body_size|client_body_(buffer_size|temp_path)'
典型错误日志是:
client intended to send too large body: 83886080 bytes
nginx -T 会展开主配置及 include 的文件,比只查看 nginx.conf 更可靠。重点确认该指令所在的 http、server 或 location 块;配置越靠近具体 location,优先级越高。
3. 排除 Ingress 和上游网关
Kubernetes 使用 NGINX Ingress 时,限制常在 Ingress 注解中,而不是工作负载容器内的 Nginx:
kubectl -n production get ingress upload-api -o yaml
kubectl -n ingress-nginx logs deploy/ingress-nginx-controller --since=15m | grep 'too large body'
确认是否存在 nginx.ingress.kubernetes.io/proxy-body-size。如果请求经过 CDN/WAF,也要在其控制台或访问日志中确认产品限制;浏览器直接命中源站成功、经过域名失败,往往说明问题在源站之前。
安全修复:只放开上传接口
不要在全局直接设置 client_max_body_size 0。无限制会让任意请求体占用代理的磁盘、内存和带宽。建议将上限设为业务允许的最大文件加上 multipart 开销,并只应用到上传路径:
server {
listen 443 ssl http2;
server_name upload.example.com;
# 其他接口继续使用较小的默认限制
client_max_body_size 2m;
location = /api/files {
# 允许最大 200 MB 的单文件上传
client_max_body_size 210m;
client_body_timeout 60s;
proxy_pass http://file_api;
proxy_request_buffering on;
proxy_read_timeout 120s;
}
}
client_max_body_size 控制 Nginx 接受的整个 HTTP 请求体,不是业务文件的精确大小;表单字段和 multipart 边界也会占用空间。client_body_timeout 限制两次读取请求体之间的等待时间,能减少慢速上传连接长期占用资源。
修改后务必先检查,再平滑加载:
sudo nginx -t
sudo systemctl reload nginx
curl -i -F "file=@upload-80m.bin" https://upload.example.com/api/files
nginx -t 失败时不得 reload。reload 会保留已有连接并由新 worker 使用新配置,通常比 restart 更适合线上变更。
大文件仍失败时的第二现场
如果 413 消失但出现 502、504 或上传中断,应继续检查请求体落盘和上游超时:
df -h /var/lib/nginx /tmp
sudo nginx -T | grep -n -E 'client_body_temp_path|proxy_(connect|send|read)_timeout|proxy_request_buffering'
sudo journalctl -u nginx --since '15 minutes ago'
默认情况下,超出 client_body_buffer_size 的请求体可能写入临时目录。临时盘空间不足、inode 耗尽或权限错误会造成新的上传失败。若使用对象存储,优先考虑让客户端通过短期签名 URL 直传;这样应用和 Nginx 不必长时间转发大流量,但签名必须限制对象前缀、Content-Type、大小和过期时间。
预防措施
- 为每个上传接口定义产品级上限,并在前端、Nginx 和应用三层保持一致;
- 将上传请求单独记录状态码、请求体大小(仅记录数值)、耗时和
request_id,便于区分 413、超时与上游失败; - 对上传接口设置鉴权、速率限制、并发限制和文件类型校验,文件先存隔离区再异步病毒扫描;
- 监控 Nginx 临时目录的空间与 inode,并对 4xx 中的 413 数量设置告警;
- 配置发布前用目标大小的测试文件走一次完整链路,覆盖 CDN、Ingress、Nginx 和应用。
总结
413 的关键不在于“把限制调大”,而在于确定拦截层、定位到具体上传路径,并按明确的业务上限安全放行。通过响应头、Nginx 错误日志和展开后的生效配置,可以迅速判断问题是否发生在应用之前;随后用范围最小的 location 配置、临时目录容量检查和上传接口防护措施,既恢复业务,也守住代理资源边界。
Discussion
评论