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