​《一次Docker部署ASP.NET Core的血泪史:Nginx Location匹配规则导致上传图片404的完整排查与解决》​
一、问题现象项目在本地开发环境一切正常上传头像、简历等文件后能立即显示。部署到云服务器Ubuntu Docker Nginx后前端界面能正常访问API能正常登录、返回数据但所有上传的图片/文件均无法访问404 Not Found。环境信息后端ASP.NET Core 6/8Docker容器前端Vue/ReactNginx容器同时担任反向代理部署方式Docker Compose网络模式容器间通过服务名通信backend:8000二、排查过程完整实录第一步确认文件是否真的上传到了容器内部bashdocker exec -it portfolio-backend /bin/bash ls -la /app/wwwroot/uploads输出结果text-rw-r--r-- 1 root root 101117 Jun 29 03:27 test.jpg结论文件确实存在不是上传失败的问题。第二步绕过Nginx直接访问后端容器bashdocker run --rm --network container:portfolio-backend appropriate/curl --head http://127.0.0.1:8000/uploads/test.jpg返回结果textHTTP/1.1 200 OK Content-Type: image/jpeg Server: Kestrel结论后端ASP.NET Core应用程序能够正常提供静态文件服务问题出在Nginx代理层。第三步检查Nginx容器能否连通Backendbashdocker exec -it portfolio-frontend ping backend -c 2输出textPING backend (172.19.0.2): 56 data bytes 64 bytes from 172.19.0.2: seq0 ttl64 time0.138 ms结论容器间网络通信正常DNS解析也无问题。第四步检查Nginx配置发现致命隐患原Nginx配置精简版nginx# 反向代理后端API location /api/ { proxy_pass http://backend:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 反向代理后端静态文件 location /uploads/ { proxy_pass http://backend:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 前端静态资源缓存策略 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 30d; add_header Cache-Control public, immutable; }问题根源Nginx 的location匹配规则中正则表达式~和~*的优先级高于普通前缀匹配。当请求/uploads/test.jpg时先被location ~* \.(jpg|...)$命中因为它是正则匹配Nginx 尝试在Nginx容器自身的root目录下找/uploads/test.jpg而不是转发给 Backend文件不存在 →404三、解决方案两种方式✅ 方案一使用^~修饰符提升优先级推荐nginx# 反向代理后端静态文件^~ 强制优先于正则 location ^~ /uploads/ { proxy_pass http://backend:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }原理^~告诉Nginx只要路径以/uploads/开头就直接命中该规则不再向下匹配任何正则表达式。❌ 方案二删除正则缓存规则不推荐如果删除location ~* \.(js|css|...)$确实能解决问题但会导致前端静态资源JS/CSS/字体等失去缓存能力严重拖慢页面加载速度不推荐。四、技术点解析Nginx Location 匹配优先级核心Nginx 的 location 匹配顺序由高到低优先级匹配类型示例1精确匹配location /path2^~修饰的字符串匹配location ^~ /uploads/3正则表达式匹配按顺序location ~ \.jpg$或location ~* \.(jpg|png)$4普通字符串匹配最长优先location /api/或location /关键点正则匹配~的优先级高于普通字符串匹配无修饰符。所以/uploads/test.jpg会因为匹配到.jpg的正则而忽略location /uploads/。五、完整 Nginx 配置生产环境推荐nginxserver { listen 8888; server_name your-domain.com; root /usr/share/nginx/html; index index.html; # Vue/React 路由SPA location / { try_files $uri $uri/ /index.html; } # API反向代理 location /api/ { proxy_pass http://backend:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 50m; } # 用户上传文件代理^~ 强制优先 location ^~ /uploads/ { proxy_pass http://backend:8000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_buffering off; # 大文件流式传输 client_max_body_size 50m; # 上传文件大小限制 } # 前端静态资源缓存正则匹配仅作用于前端自身文件 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 30d; add_header Cache-Control public, immutable; # 注意由于 ^~ /uploads/ 的存在这里不会劫持 /uploads/ 下的请求 } # Gzip 压缩 gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml; gzip_min_length 1000; }六、经验总结Nginx Location匹配顺序是“看不见的坑”不要想当然地认为/uploads/和\.jpg$会按书写顺序生效。必须熟悉匹配优先级规则。^~是解决“前缀 vs 正则”冲突的首选当你想让某个路径优先于所有正则匹配时^~是最干净、最安全的方案。保持“前端静态资源”和“后端上传文件”分离前端的 JS/CSS 适合长期缓存后端的用户头像/文件需要实时访问两者在Nginx中应分别配置各司其职。Docker环境下的排查技巧用docker exec进入容器直接看文件是否存在用docker run --rm --network container:xxx临时工具容器测试端口连通性用docker logs查看Nginx访问日志和错误日志七、彩蛋两个独立项目共用uploads目录会冲突吗如果你的服务器上部署了两个独立的ASP.NET Core项目且都通过Nginx代理但端口不同例如8888和1314那么完全不会有冲突。因为Nginx会先根据listen端口分发请求不同端口的server块相互隔离即使两个块里都有location ^~ /uploads/也各自代理到不同的后端容器互不干扰。八、相关命令速查表用途命令查看容器内文件docker exec -it 容器名 ls -la /app/wwwroot/uploads容器间网络连通性测试docker exec -it nginx容器 ping backend容器名在Nginx容器内测试后端接口docker exec nginx容器 curl -I http://backend:8000/uploads/test.jpg查看Nginx访问日志docker logs nginx容器名 --tail 50重载Nginx配置docker exec nginx容器名 nginx -s reload测试Nginx配置文件语法docker exec nginx容器名 nginx -t希望这篇记录能帮助到遇到同样问题的开发者。技术分享共同进步如果对你有帮助欢迎点赞、收藏、转发也可以在评论区交流你的排坑经历

相关新闻

认识时钟树F1

认识时钟树F1

下面按 STM32F103ZET6,也就是 F1 系列常见 72MHz 配置 来讲。你可以把“时钟树”理解成:单片机内部所有模块的“节拍来源分配图”。1. 什么是时钟树?STM32 里面很多模块都需要时钟才能工作,比如:CPU 内核运行需要时钟G…

2026/6/30 5:18:21阅读更多 →
Kioptrix Level 1 打靶记录

Kioptrix Level 1 打靶记录

1. 靶机信息靶机名称:Kioptrix Level 1 下载来源:VulnHub 目标:拿到 root 权限2. 环境准备攻击机:Kali Linux(IP:192.168.1.239) 靶机:Kioptrix Level 1(IP:1…

2026/6/30 5:18:21阅读更多 →
WinLicense 3.1.3.0 深度解析:从软件保护到许可管理的实战指南

WinLicense 3.1.3.0 深度解析:从软件保护到许可管理的实战指南

1. WinLicense 3.1.3.0 的核心定位与价值 WinLicense 3.1.3.0 是 Oreans Technologies 推出的一款集软件保护与许可管理于一体的专业工具。对于商业软件开发者来说,它解决了两个关键痛点:如何防止代码被逆向工程破解,以及如何灵活控制软件的试…

2026/6/30 5:18:21阅读更多 →
第3.5章:StarRocks实时数仓构建--基于Flink Connector与CDC的流式数据集成实战

第3.5章:StarRocks实时数仓构建--基于Flink Connector与CDC的流式数据集成实战

1. 实时数仓新选择:StarRocks与Flink的黄金组合 在数据驱动的时代,企业对实时数据分析的需求越来越强烈。想象一下,当用户在电商平台完成一笔交易,几秒钟后就能在后台看到这笔交易的统计报表;当用户在APP上点击某个按钮…

2026/6/30 10:23:53阅读更多 →
等保2.0通用安全架构设计全解析:从合规到内生安全的体系化建设实践(PPT)

等保2.0通用安全架构设计全解析:从合规到内生安全的体系化建设实践(PPT)

导语:网络安全等级保护2.0时代已全面到来。从2017年《网络安全法》正式确立等级保护制度法律地位,到2019年等保2.0核心标准正式发布,再到2021年《数据安全法》《个人信息保护法》相继实施——企业网络安全建设面临的法律合规压力与实战防御需…

2026/6/30 10:23:53阅读更多 →
终极指南:如何5分钟掌握WaveTools游戏优化工具

终极指南:如何5分钟掌握WaveTools游戏优化工具

终极指南:如何5分钟掌握WaveTools游戏优化工具 【免费下载链接】WaveTools 🧰鸣潮工具箱 项目地址: https://gitcode.com/gh_mirrors/wa/WaveTools 你是否曾经在玩《鸣潮》时感到帧率不够流畅?是否希望更好地管理你的抽卡记录&#xf…

2026/6/30 10:23:53阅读更多 →
Selenium显式等待配置错误:六大典型场景与修复实战

Selenium显式等待配置错误:六大典型场景与修复实战

1. 项目概述:当自动化脚本“卡住”时,我们在等什么?如果你写过Selenium自动化测试脚本,尤其是涉及动态加载内容的网页,那你一定对“等待”这个概念又爱又恨。爱的是,它能让你脚本的运行节奏和页面加载同步&…

2026/6/30 10:23:53阅读更多 →
高通UEFI架构下,ABL与XBL如何通过Protocol协同控制GPIO

高通UEFI架构下,ABL与XBL如何通过Protocol协同控制GPIO

1. 高通UEFI架构中的ABL与XBL基础认知 第一次接触高通平台的开发者,可能会对UEFI启动流程中的ABL和XBL感到困惑。简单来说,ABL(Application Boot Loader)相当于Android系统中的LK(Little Kernel)升级版&…

2026/6/30 10:23:53阅读更多 →
TB6612FNG电机驱动模块硬件调试实战:从引脚解析到电机正反转控制

TB6612FNG电机驱动模块硬件调试实战:从引脚解析到电机正反转控制

1. TB6612FNG模块基础认知 第一次拿到TB6612FNG这个小巧的电机驱动模块时,很多人会被密密麻麻的引脚吓到。其实拆解来看,它就是个"智能开关"——通过接收控制信号来安全地驱动电机。我当年在智能小车项目里用它驱动四个电机,实测连…

2026/6/30 10:18:50阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/6/30 4:03:30阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/6/30 4:36:27阅读更多 →
为什么你需要Destiny 2 Solo Enabler:技术原理与实战指南

为什么你需要Destiny 2 Solo Enabler:技术原理与实战指南

为什么你需要Destiny 2 Solo Enabler:技术原理与实战指南 【免费下载链接】Destiny-2-Solo-Enabler Repo containing the C# and XAML code for the D2SE program. Included is also the dependency for the program, and image asset. 项目地址: https://gitcode…

2026/6/30 0:02:58阅读更多 →
第六章:PowerPoint 2010 核心功能与实战应用 —— 从入门到精通

第六章:PowerPoint 2010 核心功能与实战应用 —— 从入门到精通

1. PowerPoint 2010基础操作全攻略 刚接触PowerPoint 2010时,很多人会被它复杂的界面吓到。其实只要掌握几个核心区域,就能快速上手。我最开始用PPT时,经常找不到功能按钮在哪,后来发现主要操作都集中在顶部功能区。 工作窗口主要…

2026/6/30 0:02:58阅读更多 →
XGBoost超参数实战:从理论到调优策略

XGBoost超参数实战:从理论到调优策略

1. XGBoost超参数基础认知 第一次接触XGBoost时,我被它那密密麻麻的参数列表吓到了。这感觉就像面对一架波音747的驾驶舱——每个按钮都可能有神奇的效果,但按错了就可能坠机。经过多年实战,我发现其实掌握十几个核心参数就能解决90%的问题。…

2026/6/30 0:02:59阅读更多 →