ShaneD711's Blog.

解决Nginx反向代理图片上传报500错误

2025/12/23
loading

1. 问题背景

在前端页面上传图片时,接口直接返回 500 Internal Server Error

开发环境:macOS (Apple Silicon) / Spring Boot / Nginx 1.29

业务场景:前端通过 Nginx 反向代理上传图片到后端服务器。

前端请求地址:http://localhost:8080/api/upload/blog (Nginx 监听端口)

后端真实地址:http://localhost:8081/upload/blog (Spring Boot 服务端口)

2. 故障现象

第一反应是去检查 IDEA 控制台的 Spring Boot 日志。然而后端控制台静悄悄的,没有任何请求进入的痕迹,也没有任何报错日志。

这意味着:请求在到达 Controller 之前就已经“暴毙”了。

3. 排查过程

3.1 控制变量法

为了确定故障节点,我决定绕过 Nginx,直接对后端服务发起请求。

  • 测试 A(直连后端):

    使用 Apifox 直接请求 http://localhost:8081/upload/blog。

    • 结果:上传成功,图片正常保存,后端日志正常打印。
    • 推论:Java 后端代码逻辑无误,文件保存路径及权限配置正确。
  • 测试 B(走 Nginx 代理):

    使用 Apifox 请求 http://localhost:8080/api/upload/blog。

    • 结果:依然报 500 错误。
    • 推论:问题百分之百出在 Nginx 网关层

3.2 检查 Nginx 配置

初步怀疑是 client_max_body_size 限制导致,或者是 proxy_pass 的路径重写规则写错了。

检查 nginx.conf:

1
2
3
4
location /api/ {
proxy_pass http://webservers/; # 路径剥离,逻辑正确
client_max_body_size 10m; # 大小限制已放开
}

配置看起来没有逻辑硬伤,排除配置语法错误。

3.3 查看 Nginx 错误日志

既然是 Nginx 报的 500,那么 Nginx 自身的错误日志一定有记录。在 Mac 终端执行:

1
tail -f /opt/homebrew/var/log/nginx/error.log

再次发起上传请求,终端瞬间捕获到了关键报错:

1
2025/12/23 13:51:24 [crit] 60314#0: *29425 open() "/opt/homebrew/var/run/nginx/client_body_temp/0000000026" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "POST /api/upload/blog HTTP/1.1", ...

错误信息open() ... client_body_temp/0000000026 failed (13: Permission denied)

4. 根因分析

为什么上传一个几十 KB 的图片会报“权限拒绝”?

  1. Nginx 的请求体缓冲机制:

    Nginx 在处理 POST 请求(尤其是 multipart/form-data 类型)时,如果请求体大小超过了内存缓冲区(client_body_buffer_size),或者为了保证传输稳定性,它会将请求体先写入一个临时文件。

    这个临时文件的存放目录就是日志中显示的 client_body_temp。

  2. 操作系统权限隔离:

    在 macOS (Homebrew 安装) 环境下:

    • client_body_temp 目录可能是在安装时由 root 或其他高权限用户创建的。
    • 而 Nginx 的 Worker 进程(实际处理请求的进程)通常是以 nobody 或当前普通用户身份运行的。
    • 冲突点:低权限的 Worker 进程试图向高权限的目录写入临时文件 0000000026,被操作系统内核拦截,导致 Nginx 进程处理失败,直接向前端抛出 500 错误。

这解释了为什么后端 Java 代码没有任何反应——请求还没走出 Nginx 的大门就已经因为写不了临时盘而崩溃了。

5. 解决方案

找到原因后,解决思路非常清晰:赋予 Nginx 进程对临时目录的读写权限。

步骤一:

在终端执行以下命令,将 Nginx 运行目录的权限完全放开:

1
2
# 修改 Nginx 运行时目录权限为 777 (读/写/执行)
sudo chmod -R 777 /opt/homebrew/var/run/nginx/

步骤二:清理旧缓存(可选)

为了防止残留的错误文件影响,可以清理一下临时目录:

1
sudo rm -rf /opt/homebrew/var/run/nginx/client_body_temp/*

步骤三:重载配置

1
nginx -s reload

执行完上述操作后,再次通过 http://localhost:8080/api/upload/blog 上传图片,问题解决,请求顺利透传至后端。

6. 总结与反思

这次排查经历给我带来了两点重要的技术启示:

  1. 不要忽视中间件的日志:

    当后端业务代码没有任何异常日志时,不要死磕代码逻辑。如果链路中存在 Nginx、网关等中间件,必须第一时间去查看中间件的 error.log。日志往往能直接告诉你真相。

  2. 环境差异与权限意识:

    相比于 Windows 开发环境,Mac 和 Linux 对文件系统权限的管理更加严格。在使用 Nginx、Docker、Redis 等需要落盘的中间件时,必须时刻关注运行用户与目标目录的权限匹配情况。

CATALOG
  1. 1. 1. 问题背景
  2. 2. 2. 故障现象
  3. 3. 3. 排查过程
    1. 3.1. 3.1 控制变量法
    2. 3.2. 3.2 检查 Nginx 配置
    3. 3.3. 3.3 查看 Nginx 错误日志
  4. 4. 4. 根因分析
  5. 5. 5. 解决方案
    1. 5.1. 步骤一:
    2. 5.2. 步骤二:清理旧缓存(可选)
    3. 5.3. 步骤三:重载配置
  6. 6. 6. 总结与反思