人生理想,技术交流加Q:411239339

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

浏览:15次阅读
没有评论

共计 2908 个字符,预计需要花费 8 分钟才能阅读完成。

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

告别遗忘,拥抱高效人生!

上传成功,却看不到头像?文件明明存在,访问却 404?这篇文章带你深入 Nginx 的 location 匹配陷阱。

问题现象

周末在生产环境测试头像上传功能,操作一切正常:

  1. 点击上传头像 ✅
  2. 选择图片 ✅
  3. 提示 " 上传成功 " ✅
  4. 刷新页面 … 头像不见了

打开浏览器控制台,赫然一个红色的 404

GET https://www.guozige.com/media/avatars/user18_d07403b000e049228e100fa6f5ff4b42.png 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 的优先级陷阱

后来在 Claude Code 的定位下,发现了根本原因:仔细检查完整的 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 在 " 截胡 "!


正文完
创作不易,扫码加点动力
post-qrcode
 0
果较瘦
版权声明:本站原创文章,由 果较瘦 于2026-02-03发表,共计2908字。
转载说明:除特殊说明外本站文章皆由果较瘦原创发布,转载请注明出处。