A file provider wrapper for ASP.NET Core that enables runtime modification of static files. Useful for rewriting HTML base href attributes when hosting under a path base, while preserving proper HTTP caching headers.
When hosting ASP.NET Core applications (especially SPAs) under a path base like /myapp, you often need to dynamically rewrite the <base href="/"> tag in your HTML to match the deployment path. Traditional solutions either:
- Require build-time configuration (different builds for different deployments)
- Use middleware that breaks HTTP caching (loses ETags, LastModified headers)
- Manually copy and modify files on application startup
FileOverlay solves this by creating a transparent file provider overlay that:
- ✅ Modifies files at runtime
- ✅ Preserves all HTTP caching headers (ETags, LastModified)
- ✅ Works seamlessly with ASP.NET Core's static file middleware
- ✅ Supports any IFileProvider source
dotnet add package FileOverlayHere's a complete example showing how to rewrite the base href in your HTML files to use the application's PathBase:
usingFileOverlay;varbuilder=WebApplication.CreateBuilder(args);varapp=builder.Build();// Get the PathBase from configuration or environmentvarpathBase=app.Configuration["PathBase"]??"/";// Create an overlay that rewrites base href in index.htmlvarfileProvider=app.Environment.WebRootFileProvider.WithBaseHrefRewrite(pathBase:pathBase,"index.html"// Can specify multiple files: "index.html", "about.html", etc.);// Use the overlay file provider for static filesapp.UseStaticFiles(newStaticFileOptions{FileProvider=fileProvider});app.Run();For a typical SPA setup with client-side routing, you'll want to use the full middleware pipeline with MapFallbackToFile:
usingFileOverlay;varbuilder=WebApplication.CreateBuilder(args);varapp=builder.Build();// Get the PathBase from configuration or environmentvarpathBase=app.Configuration["PathBase"]??"/";// Create an overlay that rewrites base href in index.htmlvarfileProvider=app.Environment.WebRootFileProvider.WithBaseHrefRewrite(pathBase:pathBase,"index.html");// Optional (relevant if there are api endpoints that are registered)app.UsePathBase(pathBase).UseRouting()// Must be called explicitly for PathBase to have effect, see https://andrewlock.net/using-pathbase-with-dotnet-6-webapplicationbuilder/#option-1-controlling-the-location-of-userouting-
app.UseDefaultFiles().UseStaticFiles(newStaticFileOptions{FileProvider=fileProvider});app.MapFallbackToFile("index.html",newStaticFileOptions{FileProvider=fileProvider});app.Run();This ensures that:
- Static files are served with the overlayed file provider
- Client-side routes (like
/products,/about) fall back to the transformedindex.html - The base href is correctly rewritten in all scenarios
If your index.html contains:
<!DOCTYPE html><html><head><basehref="/" /><title>My App</title></head><body><!-- Your app content --></body></html>And you set PathBase to /myapp, the served HTML will automatically become:
<!DOCTYPE html><html><head><basehref="/myapp/" /><title>My App</title></head><body><!-- Your app content --></body></html>For more complex scenarios, you can use the low-level API to create custom file transformations:
varoverlay=newOverlayFileProvider(app.Environment.WebRootFileProvider);// Create an overlay for a specific filevarindexFile=overlay.CreateOverlay("index.html");// Apply custom transformationsindexFile.TransformContent(content =>{// Replace any placeholder with runtime valuescontent=content.Replace("{{API_URL}}",app.Configuration["ApiUrl"]);content=content.Replace("{{VERSION}}",app.Configuration["Version"]);returncontent;});// Use the overlay for serving static filesapp.UseStaticFiles(newStaticFileOptions{FileProvider=overlay});- Overlay Creation: When you call
CreateOverlay(), FileOverlay creates a physical copy of the file in a temporary directory - Transformation: You can then transform the content using
TransformContent() - Transparent Serving: The overlay provider intercepts requests for overlayed files and serves the modified versions
- Cache Preservation: The overlay preserves the original file's
LastModifiedtimestamp, ensuring proper HTTP caching with ETags
Files that are not overlayed are served directly from the original provider without any overhead.
- Runtime Configuration: No need for different builds per environment
- Proper HTTP Caching: Maintains ETags and LastModified headers for optimal performance
- SPA Friendly: Perfect for SPAs deployed under path bases
- Flexible: Works with any
IFileProviderimplementation - Minimal Overhead: Only specified files are copied and modified
// App 1 at /app1app.Map("/app1", app1 =>{varfileProvider=env.WebRootFileProvider.WithBaseHrefRewrite("/app1","index.html");app1.UseStaticFiles(newStaticFileOptions{FileProvider=fileProvider});});// App 2 at /app2app.Map("/app2", app2 =>{varfileProvider=env.WebRootFileProvider.WithBaseHrefRewrite("/app2","index.html");app2.UseStaticFiles(newStaticFileOptions{FileProvider=fileProvider});});MIT
Contributions are welcome! Please open an issue or submit a pull request on GitHub.