树莓派Nginx+Kestrel部署Blazor后无法访问wwwroot静态文件求助
我是Blazor新手,在树莓派(Raspberry Pi OS Lite 64位)上搭建了基于.NET Core 8.0、Nginx+Kestrel的服务器,已成功部署可与Web API通信的BlazorWebApp前端,但wwwroot内的图片、JavaScript文件无法加载,仅根目录下的app.css能正常访问,浏览器调试显示这些资源报404错误。
这个问题困扰了我数周,试过调整Program.cs、Nginx、Kestrel的各类配置,修改wwwroot的权限和所有者(root、www-data),甚至尝试builder.WebHost.UseStaticWebAssets()和app.UseStaticFiles()的多种写法均无效。Nginx日志为空,NLog也未提供有效信息,网上找不到针对树莓派的解决方案,纠结是否要换成Apache。
当前配置片段
Nginx配置(默认配置片段)
#rasp Bank Statement (Frontend) location /bank/ { root /var/www/app/BlazorBankStatement/wwwroot; proxy_pass http://192.168.0.156:5023; include proxy_params; access_log /var/log/nginx/srv.local-bank-access.log; error_log /var/log/nginx/srv.local-bank-error.log; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_headers_hash_max_size 512; proxy_headers_hash_bucket_size 128; }
Kestrel服务文件
[Unit] Description= FrontEnd BankStatement After=network.target [Service] User=www-data Group=www-data WorkingDirectory=/var/www/app/BlazorBankStatement/ ExecStart=/usr/local/bin/dotnet /var/www/app/BlazorBankStatement/BlazorFront.dll Restart=always RestartSec=10 SyslogIdentifier=YourApp Environment=ASPNETCORE_ENVIRONMENT=Production Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false Environment=ASPNETCORE_URLS=http://192.168.0.156:5023 [Install] WantedBy=multi-user.target
Program.cs代码(已多次调整,略显混乱)
using BlazorFront.Components; using BlazorFront.Service; using BlazorFront.Service.Interface; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.StaticFiles; using NLog.Extensions.Logging; using System.Net; var builder = WebApplication.CreateBuilder(args); // Adding Nlog to the pipeline builder.Logging.AddNLog(); // Configure Kestrel to listen on all network interfaces on port 5023 // Adds the UseStaticWebAssets middleware to serve static files, including CSS, in production if (!builder.Environment.IsDevelopment()) { builder.WebHost.ConfigureKestrel(options => { options.Listen(IPAddress.Parse("192.168.0.156"), 5023); }); builder.WebHost.UseStaticWebAssets(); //builder.Services.AddDirectoryBrowser(); } // Add services to the container. builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); // WEB API ADDRESS (change according to your deployment) builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("http://192.168.0.156:5020") }); //Download Service builder.Services.AddScoped<IFileDownloadService, FileDownloadService>(); var app = builder.Build(); app.UseStaticFiles(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseExceptionHandler("/Error", createScopeForErrors: true); app.UseHsts(); } app.UseHttpsRedirection(); app.UseAntiforgery(); app.MapRazorComponents<App>().AddInteractiveServerRenderMode(); //Configuration of forwarded header to work with reverse proxy... Ngnix app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); //REMOVED TO AVOID AUTHENTICATION ERROR IN Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider ON Raspbery PI //app.UseAuthentication(); //app.UseFileServer(); //var provider = new FileExtensionContentTypeProvider(); //provider.Mappings["{EXTENSION}"] = "{CONTENT TYPE}"; //app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = provider }); //app.UseFileServer(enableDirectoryBrowsing: true); app.Run();
排查与解决建议
1. 修正Nginx配置的核心冲突
当前location /bank/同时使用root和proxy_pass,导致请求分流:部分资源被Nginx直接读取,部分被转发给Kestrel,这是404的主要原因。两种修正方案:
方案一:统一转发所有请求给Kestrel
直接删除root配置,让Kestrel处理所有请求(包括静态资源):
location /bank/ { proxy_pass http://192.168.0.156:5023; include proxy_params; access_log /var/log/nginx/srv.local-bank-access.log; error_log /var/log/nginx/srv.local-bank-error.log; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_headers_hash_max_size 512; proxy_headers_hash_bucket_size 128; }
方案二:Nginx单独处理静态资源
让Nginx直接返回静态文件,动态请求转发给Kestrel:
location /bank/ { proxy_pass http://192.168.0.156:5023; include proxy_params; access_log /var/log/nginx/srv.local-bank-access.log; error_log /var/log/nginx/srv.local-bank-error.log; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_headers_hash_max_size 512; proxy_headers_hash_bucket_size 128; } # 单独匹配静态资源路径,直接返回本地文件 location ~ /bank/(css|javascript|images)/ { root /var/www/app/BlazorBankStatement; rewrite ^/bank/(.*)$ /wwwroot/$1 break; # 去掉URL中的/bank前缀,匹配正确路径 expires 30d; # 开启静态资源缓存 }
2. 调整Program.cs中间件顺序
中间件执行顺序决定请求处理逻辑,UseForwardedHeaders必须放在UseStaticFiles之前,否则转发头信息无法生效,导致Kestrel处理路径错误:
var app = builder.Build(); // 先处理反向代理转发头 app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); // 再处理静态文件 app.UseStaticFiles(); // 后续中间件保持原有顺序 if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error", createScopeForErrors: true); app.UseHsts(); } app.UseHttpsRedirection(); app.UseAntiforgery(); app.MapRazorComponents<App>().AddInteractiveServerRenderMode(); app.Run();
另外,生产环境下需确保发布时生成了staticwebassets.runtime.json文件,若缺失,重新发布时添加参数:dotnet publish --configuration Release。
3. 检查文件权限与发布完整性
- 重置wwwroot权限:
sudo chown -R www-data:www-data /var/www/app/BlazorBankStatement/wwwroot sudo chmod -R 755 /var/www/app/BlazorBankStatement/wwwroot sudo find /var/www/app/BlazorBankStatement/wwwroot -type f -exec chmod 644 {} \;
- 确认发布资源完整:检查树莓派部署目录的
wwwroot/javascript、images等子目录是否和本地项目一致,若缺失则重新发布。
4. 修复Nginx日志为空问题
日志为空通常是权限不足或请求未匹配到当前location:
# 修复日志目录权限 sudo chown www-data:adm /var/log/nginx sudo chmod 755 /var/log/nginx # 重启Nginx sudo systemctl restart nginx
之后访问静态资源,查看日志中的请求路径,确认是否符合预期。
内容的提问来源于stack exchange,提问作者Lauris

