事情的起因很简单:
有个 .NET 项目,要部署到 CentOS 7 服务器上,通过 https://example.com/ltwx/ 这个子目录来访问。
网络架构也不复杂——用户访问域名,经过 CloudWAF → ELB → Nginx(8082) → dotnet(5002),三跳而已。
结果呢?技术支持折腾了大半天,问题一个接一个。我接手后逐一排查,总算全搞定了。
今天就把这 4 个坑记下来,从现象到排查到修复,下次谁再遇到,至少不用从头踩起。
坑一:.NET 10 降级 .NET 6,编译报错
项目开发时用的是 .NET 10,服务器只能装 .NET 6 运行时,那就降级吧。
改 WebSite.csproj 里的 TargetFramework,NuGet 包从 10.x 降到 6.x,dotnet-tools.json 里的 dotnet-ef 也从 10.0.8 降到 6.0.36。一切都很常规。
结果报错了。点开一看:
// Utils/CorsPolicyBuilderExtensions.cs
return origins is ["*"] ? policy.AllowAnyOrigin() : policy.WithOrigins(origins);
is ["*"] 是 C# 11 的列表模式匹配语法,.NET 6 默认 C# 10,不认识。不过整个项目 44 个 .cs 文件,只此一处用了 C# 11 语法,改成经典写法就行:
return origins.Length == 1 && origins[0] == "*" ? policy.AllowAnyOrigin() : policy.WithOrigins(origins);
坑二:CentOS 7 上首页直接 500
publish 上传,systemctl restart wx,打开首页——500。
先看日志:
journalctl -u wx -f
输出:
System.TypeInitializationException: The type initializer for 'CMS.Services.ArticleService' threw an exception.
---> System.TimeZoneNotFoundException: The time zone ID 'China Standard Time' was not found on the local computer.
一目了然。China Standard Time 是 Windows 的时区 ID,Linux 下中国时区是 Asia/Shanghai。
用 grep 确认涉哪些文件:
grep -rn "China Standard Time" .
3 个文件:ArticleService.cs、MessageService.cs、SinglePageService.cs,都在 static 构造函数里硬编码了这个值。改成跨平台兼容写法:
private static TimeZoneInfo GetChinaTimeZone()
{
try
{
return TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
}
catch (TimeZoneNotFoundException)
{
return TimeZoneInfo.FindSystemTimeZoneById("Asia/Shanghai");
}
}
重新 dotnet build,上传,重启。首页正常了。
坑三:子路径 /ltwx/ 下全部 404
这才是真正的大 BOSS。
访问 https://example.com/ltwx/,页面能打开,但所有 CSS 没了、图片裂了、导航链接点了全是 404。
F12 看源码:
<link href="/css/index.css">
<a href="/media">媒体</a>
全部缺了 /ltwx/ 前缀。
第一步:直连 dotnet 排查
# 不带 /ltwx/
curl -s http://localhost:5002/ | head -10
# 输出:<link href="/css/init.css" → 没有 /ltwx/,正常,dotnet 不知道有代理
# 带 /ltwx/
curl -s http://localhost:5002/ltwx/ | head -10
# 输出:<link href="/ltwx/css/init.css" → 有 /ltwx/,正确!
这说明 UsePathBase 代码没问题,问题在 Nginx 把 /ltwx/ 剥离了。
第二步:确认 Nginx 配置
cat /usr/local/nginx/conf/nginx.conf | grep -A 10 "location /ltwx/"
原配置:
location /ltwx/ {
proxy_pass http://localhost:5002/; # ⚠️ 尾部 "/" 会剥离 /ltwx/
}
Nginx 规则:proxy_pass 尾部有斜杠时会剥离匹配的 location 前缀。 请求链路变成:
用户请求 /ltwx/ → Nginx 剥离 → dotnet 收到 / → UsePathBase 不生效 → 路径无前缀
第三步:修 Nginx
location /ltwx/ {
proxy_pass http://localhost:5002/ltwx/; # 保留 /ltwx/
}
第四步:修代码
光修 Nginx 还不够——视图里的硬编码路径 /media、/task 不会被自动加上前缀,必须改。
全局搜:
grep -rn 'href="/' --include="*.cshtml" .
共 46 处,全部改成 ~ 前缀:
<!-- 改前 -->
<a href="/media">媒体</a>
<link href="/lib/bootstrap/css/bootstrap.min.css">
<!-- 改后 -->
<a href="~/media">媒体</a>
<link href="~/lib/bootstrap/css/bootstrap.min.css">
同时在 appsettings.json 加上:
"PathBase": "/ltwx"
Program.cs:
var pathBase = builder.Configuration.GetValue<string>("PathBase");
if (!string.IsNullOrEmpty(pathBase))
app.UsePathBase(pathBase);
第五步:验证
curl -s http://localhost:5002/ltwx/ | head -5
# 确认链接都带 /ltwx/ ✅
curl -s http://localhost:8082/ltwx/ | head -5
# 通过 Nginx 也正常 ✅
重新 publish、上传、重启服务,通过域名访问——页面正常。
坑四:图片上传成功,展示不出来
文章编辑页能上传图片了,但返回的 URL 点开就是 404。
第一步:确认文件是否写入
ls -la /data/ltwx/wwwroot/uploads/images/
# 有文件!d771ff79-066f-481a-95ff-578aac5c3c34.png ✅
第二步:逐层测试访问
# 直连 Nginx 8082
curl -I http://localhost:8082/ltwx/uploads/images/d771ff79-066f-481a-95ff-578aac5c3c34.png
# HTTP/1.1 200 OK ✅
# 通过域名访问 CSS(确认整体链路通)
curl -I https://example.com/ltwx/css/index.css
# HTTP/1.1 200 OK ✅
# 通过域名访问上传的图片
curl -I "https://example.com/ltwx/uploads/images/d771ff79-066f-481a-95ff-578aac5c3c34.png?t=1"
# HTTP/1.1 404 Not Found ❌
第三步:查 Nginx access log
tail -20 /usr/local/nginx/logs/access.log | grep uploads
# 只有本机 curl 的 200 记录,没有域名的请求
再对比正常的路径:
tail -20 /usr/local/nginx/logs/access.log | grep "/ltwx/"
# /ltwx/css/ 有来自 192.168.99.x 的请求(CloudWAF 代理 IP)
# /ltwx/ 首页也有
# 唯独 /ltwx/uploads/ 没有
结论:CloudWAF 只放行了 css、js、images 等已知路径,uploads 目录不在白名单里,请求直接被 WAF 拦截返回 404。
第四步:修复
既然 WAF 认 images,那就把上传文件也存到 images/ 下:
// FileHelper.cs
// 改前:wwwroot/uploads/images/ → 返回 /uploads/images/
// 改后:wwwroot/images/ → 返回 /images/
var uploadPath = Path.Combine(_env.WebRootPath, "images");
return $"{pathBase}/images/{fileName}";
重新发布部署,问题解决。
总结
最大的教训是:多级代理下做子路径部署,不是改一个 Nginx location 就完事的。 先用 curl 逐层排查——直连 dotnet → Nginx → 域名,确认每层的行为。最后还得留意云平台的安全策略,能顺势而为就别硬刚。
如果觉得有帮助,点个「赞」再走 。