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

69 阅读4分钟

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:

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

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

3.3 查看 Nginx 错误日志

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

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

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

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 运行目录的权限完全放开:

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

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

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

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

步骤三:重载配置

nginx -s reload

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

6. 总结与反思

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

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

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

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

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