1. 问题现象

在开发环境上传图片正常,但部署到 Linux 后,通过浏览器上传图片时,前端提示:

Request failed with status code 413

HTTP 状态码 413 Request Entity Too Large 表示服务端或请求链路中的某一层拒绝接收过大的 HTTP 请求体。它通常不是图片格式、文件保存路径或对象存储连接的问题。

2. 优先判断请求被哪一层拦截

上传链路通常如下:

浏览器 -> CDN/WAF/负载均衡/API 网关/Ingress -> Nginx -> Spring Boot -> 文件存储

413 可能由链路中任意一层返回。应先判断请求是否已进入应用:

  1. 在浏览器开发者工具的 Network 面板查看响应头和响应体。
  2. 查看 Nginx access.logerror.log
  3. 查看 Spring Boot 的访问日志、应用日志和 traceId 日志。

若 Nginx 错误日志出现以下内容,说明请求在 Nginx 被拒绝,尚未转发到 Spring Boot:

client intended to send too large body

若应用没有对应请求日志,也可作为请求未到达应用的佐证。

3. 常见根因:Nginx 默认只允许 1MB 请求体

Nginx 未配置 client_max_body_size 时,默认限制为 1m。因此,即使 Spring Boot、前端或文件存储允许更大文件,Nginx 仍会先返回 413

下面的反向代理配置没有设置请求体上限,因此会使用默认的 1m

location /api/ {
    proxy_pass http://127.0.0.1:9080;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
}

4. 不要只按原图片大小估算

有些系统不使用 multipart/form-data 上传文件,而是将文件转为 Base64 后写入 JSON 请求体;若 API 又对整个 JSON 请求体进行加密和 Base64 封装,实际 HTTP 请求体会显著大于原始文件。

本项目的通用上传接口为 JSON 请求体,其中包含 fileBase64 字段;非登录 API 还会经过 SM4 加密和安全信封封装。因此,近似关系为:

原始图片大小
  * 4 / 3                 (图片转 Base64)
  * 4 / 3                 (加密后的请求数据再次 Base64)
  = 约 1.78 倍原始图片大小

JSON 字段、签名、随机数和填充会额外增加少量字节。以原图大小为 10MB 为例,最终 HTTP 请求体约为 17.8MB,不能将 Nginx 上限仅设置为 10m15m

5. 推荐的 Nginx 配置

若图片原图业务上限为 10MB,建议将上传相关 API 的 Nginx 请求体限制设置为 30m,为加密封装和后续调整保留余量。

5.1 按 /api/ 路径配置

适用于同一 API 前缀下存在多种受控上传接口的场景:

location /api/ {
    client_max_body_size 30m;

    proxy_pass http://127.0.0.1:9080;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
}

5.2 仅按上传接口配置

若希望更严格地控制其他 API 的请求体大小,可为上传接口建立更精确的 location,并保留普通 API 的原有限制:

location = /api/system/uploads {
    client_max_body_size 30m;

    proxy_pass http://127.0.0.1:9080;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
}

location /api/ {
    proxy_pass http://127.0.0.1:9080;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
}

location = /api/system/uploads 是精确匹配,优先级高于 location /api/。对于上传接口的子路径,例如文件列表、回收站操作,仍会使用 /api/ 的规则;它们通常不包含大文件请求体。

6. 生效和验证

修改配置后,先校验语法,再平滑重载:

nginx -t
nginx -s reload

也可以使用 systemd:

systemctl reload nginx

验证时应选择接近业务上限的真实图片,并同时确认:

  1. 浏览器 Network 面板中上传请求返回 2xx,不再是 413
  2. Nginx error.log 不再出现 client intended to send too large body
  3. Spring Boot 收到请求并返回业务结果。
  4. 上传后的图片可正常访问,且文件大小、格式和场景限制仍由应用层校验。

7. 不要误用 Spring multipart 配置

下面的 Spring Boot 配置只对 multipart/form-data 请求生效:

spring:
  servlet:
    multipart:
      max-file-size: 100MB
      max-request-size: 100MB

如果上传接口通过 @RequestBody 接收 JSON 和 Base64 数据,这些 multipart 限制并不能解除 Nginx 返回的 413。同理,server.tomcat.max-http-form-post-size 是表单 POST 限制,也不是该问题的直接修复手段。

应用层仍应保留按上传场景、文件类型和原始文件字节数执行的大小校验;Nginx 的 client_max_body_size 只负责允许足够大的加密请求体抵达应用。

8. 上游还有网关时的检查项

即使 Nginx 已设置 30m,若其前方仍存在其他入口组件,也必须逐层确认请求体限制不低于该值:

组件重点配置或检查项
Kubernetes NGINX Ingressnginx.ingress.kubernetes.io/proxy-body-size: "30m"
API 网关请求体大小或 payload size 限制
WAF/CDN上传请求大小、POST body 大小策略
负载均衡HTTP 请求体或代理上传限制
对象存储直传预签名上传策略和对象大小策略

最终有效限制由最小值决定。排查时应从浏览器请求开始,沿实际转发路径逐层核对,而不是仅修改其中一个 Nginx 配置文件。

9. 安全建议

不建议将 client_max_body_size 配置为 0(无限制)或设置成与业务无关的超大值。应遵循以下原则:

  1. 根据原文件上限和编码/加密膨胀比例计算入口限制。
  2. 优先仅对上传路径放宽限制,避免所有 API 都接受大请求体。
  3. 保留前端预校验、应用层文件大小校验、文件类型白名单和权限校验。
  4. 对大文件或高频上传场景补充网关限流、超时和监控告警。
  5. 当文件规模持续增大时,优先采用对象存储预签名直传,减少大文件经过应用网关和 JSON 加密接口的开销。

10. 结论

Linux 环境上传图片出现 413 时,首先检查入口代理的请求体上限。对于 Base64 + 接口加密的上传链路,应按实际 HTTP 请求体而不是原始文件大小计算容量。对本项目 10MB 图片上传场景,在 Nginx 上传 API 路径配置 client_max_body_size 30m;,并同步核对上游网关限制,是解决该问题的直接方案。