一次 413 Content Too Large 排查:真正的问题可能不在你的后端
最近在接一个 AI 视频生成接口时,碰到了一个很典型的问题。
前端发起 POST 请求后,接口没有正常返回业务结果,而是直接报:
text
Request Method: POST
Status Code: 413 Content Too Large

第一眼看到 413,很多开发者的反应都是:
文件太大了,把 Nginx 的上传限制调大不就行了?
这个思路不能说错,但只对了一部分。
因为在现在的 Web 系统里,一个请求从浏览器发出去,到真正进入业务代码,中间可能经过本地代理、CDN、WAF、Nginx、API Gateway、Ingress、负载均衡以及应用服务器。
任何一层限制了 Request Body 大小,都可能直接返回 413。
更麻烦的是,在 AI 视频生成、图生视频、多模态模型这些场景里,我们经常会把图片转换成 Base64 塞进 JSON。原本几 MB 的图片经过 Base64 编码之后会进一步膨胀,非常容易撞上网关的请求体限制。
所以这篇文章不只是讲怎么解决一个 413,而是把这类问题完整拆开。
一、413 Content Too Large 到底是什么意思?
HTTP 413 的含义其实很直接:
text
413 Content Too Large
简单理解就是:
服务器或者中间代理认为你提交的 HTTP Request Body 太大,因此拒绝继续处理。
例如前端提交:
http
POST /api/video/create
Content-Type: application/json
Body 里面可能是:
json
{
"prompt": "生成一段视频",
"image": "data:image/jpeg;base64,..."
}
如果整个 Request Body 达到了十几 MB,而其中某一层只允许 10MB,那么请求就可能直接得到:
text
413 Content Too Large
这里有一个非常重要的概念:
413 不等于你的 Java 后端拒绝了请求。
因为这个请求可能压根没有进入 Java。
二、现代系统里,一个 HTTP 请求到底经过多少层?
我们平时开发时看到的调用关系可能只有:
text
前端
↓
后端
但线上环境通常没有这么简单。
一个比较常见的请求链路可能是:
text
浏览器
↓
本机网络代理
↓
CDN / WAF
↓
负载均衡
↓
Nginx
↓
API Gateway
↓
Kubernetes Ingress
↓
Spring Boot
↓
业务代码
如果调用的是第三方 AI 服务,链路甚至可能变成:
text
自己的前端
↓
自己的后端
↓
自己的 Nginx
↓
第三方 API Gateway
↓
第三方负载均衡
↓
第三方 AI 服务
问题就在这里。
假设:
text
Nginx 最大 100MB
Spring Boot 最大 100MB
API Gateway 最大 10MB
即使前两层全部允许 100MB,只要你的请求达到:
text
15MB
到了 API Gateway 还是会被拒绝。
所以排查 413 最重要的不是一上来就改配置,而是回答一个问题:
到底是谁返回的 413?
只要找到这一层,问题基本就解决一半了。
三、第一步:先确认请求到底有多大
遇到 413,我通常不会第一时间登录服务器。
先打开浏览器开发者工具:
text
F12
→ Network
→ 找到失败请求
重点查看:
text
Request Headers
Request Payload
Content-Length
Content-Type
尤其注意 Content-Length。
例如:
text
Content-Length: 12582912
换算一下:
text
12582912 Byte
≈ 12 MB
如果请求已经达到十几 MB,就要开始检查请求体里面到底装了什么。
对于普通 JSON API:
json
{
"userId": 10001,
"prompt": "生成一个视频"
}
理论上只有几 KB。
突然变成十几 MB,通常意味着里面塞进去了:
text
Base64 图片
Base64 视频
Base64 音频
大文件
超长文本
二进制内容
AI 多模态接口尤其容易出现这个问题。
四、最容易被忽略的坑:Base64 会让文件变大
这是这次问题里非常值得注意的一点。
假设有一张图片:
text
原始 JPG:6 MB
开发的时候为了方便,直接:
text
图片
↓
读取二进制
↓
Base64
↓
塞进 JSON
↓
POST
看起来很方便,但 Base64 并不是无损的"体积转换"。
Base64 通常会带来大约三分之一左右的体积膨胀。
也就是说:
text
6 MB JPG
↓
Base64
↓
大约 8 MB
如果是图生视频接口,需要同时上传:
text
首帧:6 MB
尾帧:7 MB
原始文件一共:
text
13 MB
转换成 Base64 后,很可能已经接近:
text
17 MB+
再加上:
text
JSON 结构
Prompt
模型参数
其他素材
请求元数据
整个 HTTP Body 很容易超过十几 MB甚至几十 MB。
这时候如果网关限制是:
text
10 MB
那么请求还没有真正进入 AI 服务,就已经被网关挡住了。
五、为什么 AI 视频接口特别容易出现 413?
传统业务接口一般不会传特别大的 Request Body。
比如:
json
{
"username": "test",
"page": 1,
"pageSize": 20
}
这种请求可能连 1KB 都不到。
但现在 AI 应用越来越多之后,请求体发生了明显变化。
例如:
文生视频
通常只有:
text
Prompt
模型名称
视频长度
分辨率
比例
请求不会特别大。
但图生视频就完全不同了。
可能包含:
text
首帧图片
尾帧图片
参考人物图片
参考物体图片
虚拟素材
角色参考图
风格参考图
如果全部采用 Base64:
text
JSON Request
├── prompt
├── first_frame_base64
├── last_frame_base64
├── reference_image_1_base64
├── reference_image_2_base64
└── parameters
请求体可能瞬间从:
text
5 KB
膨胀到:
text
20 MB
50 MB
甚至 100 MB
这也是为什么很多 AI API 更推荐传:
text
URL
而不是直接传:
text
Base64
六、如果是自己的 Nginx,怎么解决?
如果最终确认 413 是自己的 Nginx 返回的,那么处理就比较简单了。
首先检查当前配置:
bash
nginx -T | grep client_max_body_size
如果没有相关配置,可以在 http、server 或 location 中增加限制。
例如:
nginx
server {
client_max_body_size 100M;
location / {
proxy_pass http://backend;
}
}
也可以全局配置:
nginx
http {
client_max_body_size 100M;
}
修改完成后不要直接重启。
先检查:
bash
nginx -t
确认配置没有问题后:
bash
nginx -s reload
如果 Nginx 跑在 Docker 中,可以执行类似:
bash
docker exec nginx nginx -t
docker exec nginx nginx -s reload
生产环境里建议优先 reload,而不是为了修改一个参数直接把整个服务停掉再启动。
七、Spring Boot 也可能有限制
如果请求确实已经进入 Spring Boot,但是上传文件时报错,那么还需要检查应用层。
例如:
yaml
spring:
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
这里两个参数不要混淆。
max-file-size 控制的是:
text
单个文件最大允许大小
而 max-request-size 控制的是:
text
整个 multipart 请求最大大小
例如一次上传:
text
图片 A:8MB
图片 B:8MB
图片 C:8MB
虽然每一个都没有超过:
text
10MB
但整个请求已经:
text
24MB+
如果:
text
max-file-size: 10MB
max-request-size: 20MB
依然可能失败。
所以多文件上传场景一定要同时关注两个参数。
八、最关键的问题:如果是第三方 API 返回 413 呢?
这才是很多开发者最容易绕进去的地方。
假设调用链路是:
text
自己的系统
↓
第三方视频生成 API
第三方限制:
text
最大 Request Body:10MB
你的请求:
text
18MB
这时候你去修改:
text
自己的 Nginx:100MB
自己的 Spring Boot:100MB
自己的 Tomcat:100MB
全部没有意义。
因为真正拒绝请求的是:
text
第三方网关
这个时候真正应该改变的是:
请求方式,而不是服务器参数。
九、最推荐的方案:不要直接把大文件塞进 JSON
对于 AI 视频、AI 图片、OCR、语音识别等涉及媒体素材的 API,我现在更推荐这种架构:
text
浏览器
↓
上传图片
↓
对象存储
↓
获得文件 URL
↓
调用 AI API
↓
只提交 URL
例如原来:
json
{
"prompt": "让画面中的人物向前行走",
"image": "data:image/jpeg;base64,/9j/4AAQSk..."
}
改成:
json
{
"prompt": "让画面中的人物向前行走",
"image_url": "https://static.example.com/images/xxx.jpg"
}
这时候 HTTP Request Body 可能从:
text
8 MB
直接下降到:
text
几 KB
差距非常明显。
十、为什么 URL 方案通常比 Base64 更合理?
除了避免 413,它还有很多额外收益。
1. 请求更轻
原来:
text
POST 20MB JSON
现在:
text
POST 3KB JSON
API Gateway、Nginx、应用服务器的压力都会明显下降。
2. 减少内存占用
Base64 通常需要经历:
text
读取文件
↓
加载到内存
↓
转换 Base64
↓
生成 String
↓
拼装 JSON
↓
HTTP 序列化
如果一个请求有多个高清素材,并发再一高,内存压力会非常明显。
3. 降低网络重复传输
如果素材已经存在对象存储,再转换成 Base64 从业务服务器上传一次,本质上属于重复搬运。
URL 模式则可以让目标服务自己拉取资源。
4. 更适合异步任务
AI 视频生成通常不是同步完成,而是:
text
提交任务
↓
返回 taskId
↓
模型异步生成
↓
查询任务状态
↓
返回视频 URL
既然整个架构本身就是资源 URL + 异步任务,那么输入素材同样采用 URL 往往更加自然。
十一、对象存储也不能直接"裸奔"
当然,把 Base64 改成 URL 并不意味着直接把所有素材永久公开。
实际生产环境可以采用:
text
临时签名 URL
例如:
text
上传对象存储
↓
生成 30 分钟有效的临时 URL
↓
提交 AI 任务
↓
AI 服务下载素材
↓
URL 自动失效
这样同时解决:
text
大 Request Body
安全性
访问控制
生命周期管理
对于用户上传的图片、视频等内容,比永久公网 URL 更合理。
十二、看到本地代理地址,不代表代理就是罪魁祸首
还有一个很容易造成误判的现象。
浏览器 Network 中可能看到:
text
Remote Address:
127.0.0.1:xxxx
很多人的第一反应是:
是不是代理软件把我的请求限制了?
不一定。
如果电脑启用了系统代理,那么浏览器实际连接的确实可能是:
text
Browser
↓
127.0.0.1:代理端口
↓
Internet
所以 DevTools 显示本地地址很正常。
这只能证明:
浏览器通过本机代理发送了这个请求。
不能证明:
413 是本机代理返回的。
如果想快速排除代理,可以临时关闭代理后测试,或者使用 curl 绕过代理。
例如:
bash
curl --noproxy "*" \
-X POST \
"https://api.example.com/v1/task"
也可以临时取消环境变量:
bash
unset http_proxy
unset https_proxy
unset HTTP_PROXY
unset HTTPS_PROXY
如果绕过代理之后依然返回 413,那么问题大概率在服务端链路。
十三、怎么快速判断 413 到底是哪一层返回的?
我现在排查这类问题,一般按照下面的顺序。
第一步:
text
检查 Request Payload
确认有没有:
text
Base64
大 JSON
文件
视频
图片
音频
第二步:
text
检查 Content-Length
先知道请求到底有多大。
第三步:
text
检查自己的后端日志
如果请求发生的时候:
text
Java 一条访问日志都没有
那么就要高度怀疑:
text
Nginx
Ingress
Gateway
CDN
WAF
第三方网关
因为请求可能根本没有进入应用。
第四步:
检查 Nginx:
bash
nginx -T | grep client_max_body_size
第五步:
检查 Spring Boot:
yaml
spring.servlet.multipart.max-file-size
spring.servlet.multipart.max-request-size
第六步:
如果是 Kubernetes:
text
Client
↓
Ingress
↓
Service
↓
Pod
还要检查 Ingress Controller 有没有 Request Body 限制。
第七步:
如果最终请求的是第三方 API:
text
查看官方接口文档
↓
确认单文件限制
↓
确认 Request Body 限制
↓
确认是否支持 URL
↓
确认是否支持 multipart
↓
确认是否允许 Base64
到这里基本就能把问题定位出来。
十四、一个很实用的判断方法:看请求有没有到业务服务
这是排查 413 时我认为最好用的技巧之一。
假设:
text
14:32:10
前端请求接口并收到:
text
413
马上查看后端 access log。
如果:
text
14:32:10 没有任何对应请求
说明请求很可能死在:
text
后端之前
接着查 Nginx access/error log。
如果 Nginx 出现:
text
client intended to send too large body
基本就可以直接锁定了。
反过来,如果 Nginx 已经成功转发:
text
Nginx
↓
Spring Boot
而 Spring Boot 日志出现 multipart 相关异常,那么继续查应用配置。
不要一看到 HTTP 错误就直接扎进 Java 代码里 Debug。
很多时候代码根本没机会运行。
十五、生产环境不要无脑把限制改成 1GB
还有一个值得单独提醒的问题。
有些开发者遇到 413 之后直接:
nginx
client_max_body_size 1024M;
问题确实可能暂时消失。
但这并不一定是一个好的解决方案。
如果你的业务本来只允许上传:
text
20MB
结果网关允许:
text
1GB
意味着异常客户端可以不断发送超大请求。
可能增加:
text
带宽占用
临时文件占用
磁盘 IO
连接占用
内存压力
网关压力
攻击面
所以正确思路应该是:
text
业务最大文件大小
↓
合理增加一定余量
↓
配置网关限制
例如业务最大允许:
text
50MB
那么可以结合实际情况配置:
text
60MB / 80MB / 100MB
而不是没有上限地往上加。
十六、AI 系统的媒体上传,建议从架构层面重新设计
如果现在正在做:
text
AI 视频
AI 绘画
数字人
图生视频
视频理解
语音识别
多模态大模型
那么最好不要继续沿用传统的小 JSON API 思维。
推荐:
text
┌──────────────┐
│ Web / App │
└──────┬───────┘
│
获取上传凭证
│
↓
┌─────────────────┐
│ Object Storage│
│ 图片 / 视频 / 音频 │
└────────┬────────┘
│
File URL
│
↓
┌─────────────┐
│ API Backend │
└──────┬──────┘
│
Prompt + File URL
│
↓
┌─────────────┐
│ AI Provider │
└──────┬──────┘
│
Task ID
│
↓
异步查询任务结果
而不是:
text
浏览器
↓
几十 MB 文件
↓
Base64
↓
巨大 JSON
↓
自己的后端
↓
再次上传第三方
后一种架构随着图片分辨率、视频大小、并发量上升,很快就会出现各种问题:
text
413
超时
内存暴涨
GC 频繁
网关压力
带宽浪费
接口响应变慢
413 很多时候只是第一个暴露出来的问题。
十七、最后总结:以后看到 413,按照这张图排查
整个排查流程可以浓缩成:
text
出现 413
│
↓
检查 Request Body 大小
│
├── 很小
│ ↓
│ 检查网关异常配置
│
└── 很大
│
↓
是否包含 Base64 / 文件?
│
├── 是
│ ↓
│ 优先考虑 URL / 对象存储
│
└── 否
↓
继续定位来源
│
↓
请求有没有进入后端?
│ │
没有 有
│ │
↓ ↓
CDN / WAF Spring Boot
Nginx Multipart
Gateway Tomcat
Ingress 应用配置
│
↓
是否第三方服务?
│
是
↓
查看第三方 Body 限制
↓
URL / Multipart / 压缩素材
写在最后
413 Content Too Large 本身并不复杂。
真正容易浪费时间的地方,是我们习惯把:
text
HTTP 请求失败
直接等价成:
text
后端代码出问题
但现在的系统链路越来越长。
一个请求从浏览器到业务代码,中间可能经过五六层甚至更多基础设施。任何一层都有能力拒绝这个请求。
特别是在 AI 视频、多模态应用快速普及之后,图片、音频、视频开始频繁进入 API 调用链路,过去那些只处理几 KB JSON 的设计方式已经不一定适用了。
所以以后再遇到 413,不要第一时间无脑把:
nginx
client_max_body_size
调到几百 MB。
先问三个问题:
第一,请求到底有多大?
第二,413 到底是谁返回的?
第三,这个大文件真的有必要跟着 JSON 一起传吗?
如果第三个问题的答案是"不一定",那么真正值得优化的可能就不是一个 Nginx 参数,而是整个媒体资源的传输方式。
很多时候:
text
Base64 塞 JSON
改成:
text
对象存储 + 临时 URL + 异步任务
不仅解决了 413,也顺手解决了后面可能出现的带宽、内存、超时、网关压力和扩展性问题。
一个 413,看起来只是一次请求失败,往深了排查,其实是在提醒你重新审视整个文件传输链路。