404 when trying to access images uploaded after the app was built

Part of a site I’m currently building allows users to upload images. These are stored in folder like /wwwroot/images/uploads/27/jim.jpg and so on.

I had a major problem, in that the site worked fine running locally, but when deployed, would not show any uploaded images. Images that were part of the Visual Studio project were served up fine, but any images uploaded afterwards would give a 404. I couldn’t see any error anywhere, and the server logs just showed the 404.

To make things even more baffling, I couldn’t access files uploaded to the /wwwroot/images folder. I could have two images sitting next to each other, and one would be served fine, whereas the other would give a 404. Muchus oddus as they probably don’t say in Latin!

I was puzzled why I had run into this issue now, when I had written plenty of sites that allowed files to be uploaded, and had ever run into the issue before.

Fast-forward past much pulling of hair and gnashing of teeth…

It turned out that the MapStaticAssets method, added in .NET9 causes a file named something like YourProjectName.staticwebassets.endpoints.json to be created and published with your app. This file contains references to all the static assets in the project. These will be served, others won’t, which is why the uploaded files weren’t being served.

I needed to add configuration when telling ASP.NET Core to serve static files. The following code in the server project’s Program.cs file did the trick…

app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions {
  FileProvider = new PhysicalFileProvider(
    Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")),
  RequestPath = "/images"
});
app.MapStaticAssets();

We call UseStaticFiles twice, once without any additional configuration, which sets up middleware specifically for serving the default wwwroot folder contents using configuration from the YourProjectName.staticwebassets.endpoints.json file.

When we add a second UseStaticFiles with custom configuration, it creates a separate middleware instance that handles only the paths specified in that configuration.

While it might seem redundant, combining both is often necessary because:

  • The default middleware handles all the static files that are part of the project (.css, .js, etc.)
  • The custom middleware specifically handles any dynamic upload folders (eg where user’s images are uploaded to a folder named after their user Id)

Technically, we could try to combine them into a single configuration, but this would require manually replicating all the default static file handling logic, which is more complex and error-prone.

We could avoid the problem altogether by not calling MapStaticAssets, but then we’d lose out on the benefits that it brings. MapStaticAssets is specific to Blazor and handles static web assets that are embedded in referenced libraries and NuGet packages.

MapStaticAssets is different from UseStaticFiles because it:

  • Maps virtual paths to embedded resources in referenced assemblies
  • Handles static assets from Razor class libraries
  • Manages the manifest of static web assets created during the build

For example, if you use a Blazor component library that includes its own CSS or images, MapStaticAssets ensures those files are accessible. Even if you’re not directly using any component libraries, it seems that it’s a good idea to keep MapStaticAssets in Blazor apps for future compatibility.

P.S. In case anyone is wondering what on Earth the picture above this blog post has to do with the article, it’s a Peugeot 404 😎

Be First to Comment

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.