找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 7|回复: 1

「原创」耗时2小时,只因两个字符:记一次让人抓狂的 Nginx 头像404排查

[复制链接]

5

主题

11

回帖

38

积分

管理员

积分
38
发表于 昨天 21:57 | 显示全部楼层 |阅读模式


有朋友知道我做了个待办清单的应用,前两周更新了用户中心的功能,但有粉丝反应说自定义头像上传失败,要我赶紧修复,当时想着不影响功能就一直没去修,趁着今天有点空就排查了一下,不查不要紧,一查吓一跳!大家可以点击下面文章去查看我的另一个公众号,可以关注一下!

[size=1em]上传成功,却看不到头像?文件明明存在,访问却404?这篇文章带你深入 Nginx 的 location 匹配陷阱。
问题现象
周末在生产环境测试头像上传功能,操作一切正常:
  • 1. 点击上传头像 ✅
  • 2. 选择图片 ✅
  • 3. 提示"上传成功" ✅
  • 4. 刷新页面... 头像不见了
打开浏览器控制台,赫然一个红色的 404:
GET 404初步排查:文件到底在不在?
作为一个"经验丰富"的开发者,我的第一反应是:文件可能没保存成功
# 进入 Docker 容器检查
$ docker exec todolist_app ls -la /app/media/avatars/
-rwxrwxr-x 1 www www 777090 user18_d07403b000e049228e100fa6f5ff4b42.png
文件存在!大小也正常(777KB)。
那问题出在哪?🤔
深入排查:Docker Volume 的坑?
等等,Docker 容器里的文件,宿主机能访问到吗?
# 检查 Docker 挂载配置
$ docker inspect todolist_app --format '{{json .Mounts}}' | python3 -m json.tool
{
    "Source": "/www/data/todolist/media",
    "Destination": "/app/media"
}
原来容器的 /app/media 挂载到了宿主机的 /www/data/todolist/media。
# 检查宿主机目录
$ ls -la /www/data/todolist/media/avatars/
-rwxrwxr-x 1 www www 777090 user18_d07403b000e049228e100fa6f5ff4b42.png
文件也在!Volume 挂载没问题。
检查 Nginx 配置
既然文件存在,那一定是 Nginx 配置的问题。看看 media 的 location 配置:
# /www/server/panel/vhost/nginx/html_www.guozige.com.conf

location /media/ {
    alias /www/data/todolist/media/;
    expires 30d;
    add_header Cache-Control "public, immutable";
}
路径完全正确!指向 /www/data/todolist/media/,和 Docker 挂载的目录一致。
我开始怀疑人生了... 🤯
转机:检查 Nginx 日志
为 media 配置了专门的日志:
$ cat /www/wwwlogs/media_access.log
# 空的!
$ cat /www/wwwlogs/media_error.log
# 也是空的!
日志是空的! 这说明请求根本没有进入 location /media/ 块!
那请求去哪了?
真相大白:正则 location 的优先级陷阱
仔细检查完整的 Nginx 配置,我发现了这段"隐藏的杀手":
# 在配置文件的后面...
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
    expires 30d;
    error_log /dev/null;
    access_log /dev/null;
}
这是一个正则表达式 location(~ 开头),匹配所有图片文件!
Nginx Location 匹配规则
Nginx 的 location 匹配优先级如下:
优先级修饰符说明示例
1=精确匹配location = /api
2^~前缀匹配(阻止正则)location ^~ /static/
3~ / ~*正则匹配location ~ \.png$
4无普通前缀匹配location /media/
关键点:正则匹配(~)优先于普通前缀匹配!
所以当请求 /media/avatars/xxx.png 时:
  • 1. Nginx 先找精确匹配 → 没有
  • 2. 再找 ^~ 前缀匹配 → 没有
  • 3. 然后找正则匹配 → 命中 ~ .*\.png$
  • 4. 普通前缀 /media/ 被跳过了!
而这个正则 location 没有指定 root 或 alias,所以使用了 server 块的默认 root:
root /www/wwwroot/todolist/frontend/dist;
最终 Nginx 去找的是:
/www/wwwroot/todolist/frontend/dist/media/avatars/xxx.png
这个路径当然不存在,所以返回 404!
解决方案
只需要一个字符:^~
# 修改前
location /media/ {
    alias /www/data/todolist/media/;
}

# 修改后
location ^~ /media/ {
    alias /www/data/todolist/media/;
}
^~ 修饰符告诉 Nginx:如果这个前缀匹配成功,不要再检查正则表达式,直接使用这个 location。
# 测试配置
$ nginx -t
nginx: configuration file /www/server/nginx/conf/nginx.conf test is successful

# 重载配置
$ nginx -s reload
刷新页面,头像终于显示了!🎉
经验总结1. Nginx Location 优先级口诀精确第一(=),前缀阻断(^~),正则优先(~),普通垫底2. 常见场景的正确配置# 静态资源目录:使用 ^~ 阻止正则匹配
location ^~ /static/ {
    alias /var/www/static/;
}

location ^~ /media/ {
    alias /var/www/media/;
}

# 通用图片缓存规则(放在最后)
location ~* \.(gif|jpg|jpeg|png|webp)$ {
    expires 30d;
}3. 排查思路问题现象 → 确认文件存在 → 检查 Nginx 配置 → 查看日志 → 分析 location 匹配4. 调试技巧
为可疑的 location 添加独立日志:
location /media/ {
    alias /var/www/media/;
    access_log /var/log/nginx/media_access.log;
    error_log /var/log/nginx/media_error.log;
}
如果日志为空,说明请求没有命中这个 location。
写在最后
这个问题让我折腾了整整 2 小时,最后的解决方案却只有 2 个字符:^~。
Nginx 的 location 匹配规则看似简单,实则暗藏玄机。希望这篇文章能帮你在遇到类似问题时,少走一些弯路。
记住:当静态资源 404 但文件确实存在时,先检查是否有正则 location 在"截胡"!




5

主题

11

回帖

38

积分

管理员

积分
38
 楼主| 发表于 昨天 22:03 来自手机 | 显示全部楼层
折腾人!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

果子博客
扫码关注微信公众号

Archiver|手机版|小黑屋|风叶林

GMT+8, 2026-2-4 09:31 , Processed in 0.121232 second(s), 28 queries .

Powered by 风叶林

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表