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 的图片会报“权限拒绝”?
-
Nginx 的请求体缓冲机制:
Nginx 在处理 POST 请求(尤其是 multipart/form-data 类型)时,如果请求体大小超过了内存缓冲区(client_body_buffer_size),或者为了保证传输稳定性,它会将请求体先写入一个临时文件。
这个临时文件的存放目录就是日志中显示的 client_body_temp。
-
操作系统权限隔离:
在 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. 总结与反思
这次排查经历给我带来了两点重要的技术启示:
-
不要忽视中间件的日志:
当后端业务代码没有任何异常日志时,不要死磕代码逻辑。如果链路中存在 Nginx、网关等中间件,必须第一时间去查看中间件的 error.log。日志往往能直接告诉你真相。
-
环境差异与权限意识:
相比于 Windows 开发环境,Mac 和 Linux 对文件系统权限的管理更加严格。在使用 Nginx、Docker、Redis 等需要落盘的中间件时,必须时刻关注运行用户与目标目录的权限匹配情况。