Next.js + Nginx 动态文件上传与 403/404 问题排查
Next.js + Nginx 动态文件上传与 403/404 问题排查
问题背景
项目使用:
- Next.js
- Nginx
- Linux(Ubuntu)
需要支持用户上传 .mid 文件。
最初将文件放在:
/public/midisongs开发环境(Windows)正常,但部署到 Linux 后出现:
- 某些文件 404
- 删除
.next后恢复 revalidatePath()无效- 中文文件名异常
- nginx alias 后出现 403
核心结论
不要把运行时上传文件放进 public
public/ 是:
构建时静态资源目录不是:
运行时动态文件存储否则会遇到:
- Next.js static manifest cache
- build cache
.next缓存- Linux 文件系统问题
正确方案
推荐目录结构
/home/ubuntu/mahirooyama-blog/
├ uploads/
│ ├ midisongs/
│ │ ├ 4b378e32280932b9.mid
│
├ app/
├ public/
├ next.config.jsNginx 配置
location /midisongs/ {
alias /home/ubuntu/mahirooyama-blog/uploads/midisongs/;
}注意:
alias 后面必须带 /
错误:
alias /home/ubuntu/mahirooyama-blog/uploads/midisongs;正确:
alias /home/ubuntu/mahirooyama-blog/uploads/midisongs/;为什么之前会 404
原因:
public/ 下动态新增文件
不会被 Next.js production 正确感知所以:
- API 能读取
- 页面列表能更新
- 但静态资源 404
删除 .next 后恢复,是因为:
重新生成了 static manifest为什么 revalidatePath() 无效
revalidatePath('/', 'layout');只能刷新:
- RSC cache
- fetch cache
- 页面缓存
不能刷新:
静态文件系统映射因为 .mid 文件不属于:
- page
- route
- fetch
而属于:
Next.js static asset server为什么 nginx 会 403
403 表示:
nginx 找到了文件
但没有权限读取不是文件不存在。
Linux 权限关键点
Linux 不仅要求:
文件可读还要求:
路径上的每一级目录都必须有 x 权限例如:
/home/ubuntu/mahirooyama-blog/uploads/midisongs/test.midnginx 必须能穿过:
/
home
ubuntu
mahirooyama-blog
uploads
midisongs正确权限配置
1. 允许目录 traversal
chmod o+x /home/ubuntu
chmod o+x /home/ubuntu/mahirooyama-blog注意:
不是:
chmod 755 /home/ubuntu只需要:
o+x即可。
2. uploads 目录权限
chmod -R 755 /home/ubuntu/mahirooyama-blog/uploads3. 文件权限
find /home/ubuntu/mahirooyama-blog/uploads \
-type f \
-exec chmod 644 {} \;4. reload nginx
sudo nginx -t
sudo systemctl reload nginx验证 nginx 是否有权限
Ubuntu 默认 nginx 用户通常是:
www-data验证:
sudo -u www-data ls \
/home/ubuntu/mahirooyama-blog/uploads/midisongs如果能列出文件:
权限已经正确查看 nginx 错误日志
最关键命令:
tail -f /var/log/nginx/error.log然后浏览器访问一次。
可以直接看到:
- permission denied
- forbidden
- no such file
等真实原因。
最终推荐架构
用户上传文件
放:
/uploads不要放:
/publicNext.js
负责:
- 页面
- API
- 数据
Nginx
负责:
- 静态文件
- 大文件
- 上传资源
通过:
alias直接读取磁盘。
推荐文件命名
不要使用原始中文文件名作为磁盘文件名。
推荐:
4b378e32280932b9.mid原始标题保存在数据库:
{
"title": "Minecraft",
"storageKey": "4b378e32280932b9.mid"
}这样可以避免:
- Unicode
- Linux 文件系统
- URL 编码
- 特殊字符
相关问题。
总结
正确做法
用户上传
↓
uploads/
↓
nginx alias
↓
/midisongs/*而不是:
用户上传
↓
public/
↓
Next.js static server