Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Static files, such as HTML, CSS, images, and JavaScript, are assets an ASP.NET Core app serves directly to clients by default.
For Blazor static files guidance, which adds to or supersedes the guidance in this article, see ASP.NET Core Blazor static files.
Serve static files
Static files are stored within the project's web root directory. The default directory is {content root}/wwwroot
, but it can be changed with the UseWebRoot method. For more information, see Content root and Web root.
The CreateBuilder method sets the content root to the current directory:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.MapStaticAssets();
app.UseAuthorization();
app.MapDefaultControllerRoute().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();
app.Run();
Static files are accessible via a path relative to the web root. For example, the Web Application project templates contain several folders within the wwwroot
folder:
wwwroot
css
js
lib
Consider an app with the wwwroot/images/MyImage.jpg
file. The URI format to access a file in the images
folder is https://<hostname>/images/<image_file_name>
. For example, https://localhost:5001/images/MyImage.jpg
Map Static Assets routing endpoint conventions (MapStaticAssets
)
Creating performant web apps requires optimizing asset delivery to the browser. Possible optimizations with MapStaticAssets include:
- Serve a given asset once until the file changes or the browser clears its cache. Set the ETag and Last-Modified headers.
- Prevent the browser from using old or stale assets after an app is updated. Set the Last-Modified header.
- Set up proper caching headers.
- Use Caching Middleware.
- Serve compressed versions of the assets when possible. This optimization doesn't include minification.
- Use a CDN to serve the assets closer to the user.
- Fingerprinting assets to prevent reusing old versions of files.
MapStaticAssets
:
- Integrates the information gathered about static web assets during the build and publish process with a runtime library that processes this information to optimize file serving to the browser.
- Are routing endpoint conventions that optimize the delivery of static assets in an app. It's designed to work with all UI frameworks, including Blazor, Razor Pages, and MVC.
MapStaticAssets
versus UseStaticFiles
MapStaticAssets is available in ASP.NET Core in .NET 9.0 and later. UseStaticFiles must be used in versions prior to .NET 9.0.
UseStaticFiles
serves static files, but it doesn't provide the same level of optimization as MapStaticAssets
. MapStaticAssets
is optimized for serving assets that the app has knowledge of at runtime. If the app serves assets from other locations, such as disk or embedded resources, UseStaticFiles
should be used.
Map Static Assets provides the following benefits that aren't available when calling UseStaticFiles
:
- Build-time compression for all the assets in the app, including JavaScript (JS) and stylesheets but excluding image and font assets that are already compressed. Gzip (
Content-Encoding: gz
) compression is used during development. Gzip with Brotli (Content-Encoding: br
) compression is used during publish. - Fingerprinting for all assets at build time with a Base64-encoded string of the SHA-256 hash of each file's content. This prevents reusing an old version of a file, even if the old file is cached. Fingerprinted assets are cached using the
immutable
directive, which results in the browser never requesting the asset again until it changes. For browsers that don't support theimmutable
directive, amax-age
directive is added.- Even if an asset isn't fingerprinted, content based
ETags
are generated for each static asset using the fingerprint hash of the file as theETag
value. This ensures that the browser only downloads a file if its content changes (or the file is being downloaded for the first time). - Internally, the framework maps physical assets to their fingerprints, which allows the app to:
- Find automatically-generated assets, such as Razor component scoped CSS for Blazor's CSS isolation feature and JS assets described by JS import maps.
- Generate link tags in the
<head>
content of the page to preload assets.
- Even if an asset isn't fingerprinted, content based
- During Visual Studio Hot Reload development testing:
- Integrity information is removed from the assets to avoid issues when a file is changed while the app is running.
- Static assets aren't cached to ensure that the browser always retrieves current content.
Map Static Assets doesn't provide features for minification or other file transformations. Minification is usually handled by custom code or third-party tooling.
The following features are supported with UseStaticFiles
but not with MapStaticAssets
:
- Serving files from disk or embedded resources, or other locations
- Serve files outside of web root
- Set HTTP response headers
- Directory browsing
- Serve default documents
FileExtensionContentTypeProvider
- Serve files from multiple locations
- Serving files from disk or embedded resources, or other locations
- Serve files outside of web root
- Set HTTP response headers
- Directory browsing
- Serve default documents
FileExtensionContentTypeProvider
- Serve files from multiple locations
Serve files in web root
The default web app templates call the MapStaticAssets method in Program.cs
, which enables static files to be served:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.MapStaticAssets();
app.UseAuthorization();
app.MapDefaultControllerRoute().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();
app.Run();
The parameterless MapStaticAssets
method overload marks the files in web root as servable. The following markup references wwwroot/images/MyImage.jpg
:
<img src="~/images/MyImage.jpg" class="img" alt="My image" />
In the preceding markup, the tilde character ~
points to the web root.
Serve files outside of web root
Consider a directory hierarchy in which the static files to be served reside outside of the web root:
wwwroot
css
images
js
MyStaticFiles
images
red-rose.jpg
A request can access the red-rose.jpg
file by configuring the Static File Middleware as follows:
using Microsoft.Extensions.FileProviders;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles(); //Serve files from wwwroot
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
RequestPath = "/StaticFiles"
});
app.UseAuthorization();
app.MapDefaultControllerRoute().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();
app.Run();
In the preceding code, the MyStaticFiles directory hierarchy is exposed publicly via the StaticFiles URI segment. A request to https://<hostname>/StaticFiles/images/red-rose.jpg
serves the red-rose.jpg
file.
The following markup references MyStaticFiles/images/red-rose.jpg
:
<img src="~/StaticFiles/images/red-rose.jpg" class="img" alt="A red rose" />
To serve files from multiple locations, see Serve files from multiple locations.
Set HTTP response headers
A StaticFileOptions object can be used to set HTTP response headers. In addition to configuring static file serving from the web root, the following code sets the Cache-Control header:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
var cacheMaxAgeOneWeek = (60 * 60 * 24 * 7).ToString();
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers.Append(
"Cache-Control", $"public, max-age={cacheMaxAgeOneWeek}");
}
});
app.UseAuthorization();
app.MapDefaultControllerRoute().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();
app.Run();
The preceding code makes static files publicly available in the local cache for one week.
Static file authorization
The ASP.NET Core templates call MapStaticAssets before calling UseAuthorization. Most apps follow this pattern. When MapStaticAssets
is called before the authorization middleware:
- No authorization checks are performed on the static files.
- Static files served by the Static File Middleware, such as those under
wwwroot
, are publicly accessible.
To serve static files based on authorization, see Static file authorization.
Serve files from multiple locations
Consider the following Razor page which displays the /MyStaticFiles/image3.png
file:
@page
<p> Test /MyStaticFiles/image3.png</p>
<img src="~/image3.png" class="img" asp-append-version="true" alt="Test">
UseStaticFiles
and UseFileServer
default to the file provider pointing at wwwroot
. Additional instances of UseStaticFiles
and UseFileServer
can be provided with other file providers to serve files from other locations. The following example calls UseStaticFiles
twice to serve files from both wwwroot
and MyStaticFiles
:
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles"))
});
Using the preceding code:
- The
/MyStaticFiles/image3.png
file is displayed. - The Image Tag Helpers AppendVersion is not applied because the Tag Helpers depend on WebRootFileProvider.
WebRootFileProvider
has not been updated to include theMyStaticFiles
folder.
The following code updates the WebRootFileProvider
, which enables the Image Tag Helper to provide a version:
var webRootProvider = new PhysicalFileProvider(builder.Environment.WebRootPath);
var newPathProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles"));
var compositeProvider = new CompositeFileProvider(webRootProvider,
newPathProvider);
// Update the default provider.
app.Environment.WebRootFileProvider = compositeProvider;
app.MapStaticAssets();
Note
The preceding approach applies to Razor Pages and MVC apps. For guidance that applies to Blazor Web Apps, see ASP.NET Core Blazor static files.
Serve files outside wwwroot by updating IWebHostEnvironment.WebRootPath
When IWebHostEnvironment.WebRootPath is set to a folder other than wwwroot
:
- In the development environment, static assets found in both
wwwroot
and the updatedIWebHostEnvironment.WebRootPath
are served fromwwwroot
. - In any environment other than development, duplicate static assets are served from the updated
IWebHostEnvironment.WebRootPath
folder.
Consider a web app created with the empty web template:
Containing an
Index.html
file inwwwroot
andwwwroot-custom
.With the following updated
Program.cs
file that setsWebRootPath = "wwwroot-custom"
:var builder = WebApplication.CreateBuilder(new WebApplicationOptions { Args = args, // Look for static files in "wwwroot-custom" WebRootPath = "wwwroot-custom" }); var app = builder.Build(); app.UseDefaultFiles(); app.MapStaticAssets(); app.Run();
In the preceding code, requests to /
:
- In the development environment return
wwwroot/Index.html
- In any environment other than development return
wwwroot-custom/Index.html
To ensure assets from wwwroot-custom
are returned, use one of the following approaches:
Delete duplicate named assets in
wwwroot
.Set
"ASPNETCORE_ENVIRONMENT"
inProperties/launchSettings.json
to any value other than"Development"
.Completely disable static web assets by setting
<StaticWebAssetsEnabled>false</StaticWebAssetsEnabled>
in the project file. WARNING, disabling static web assets disables Razor Class Libraries.Add the following XML to the project file:
<ItemGroup> <Content Remove="wwwroot\**" /> </ItemGroup>
The following code updates IWebHostEnvironment.WebRootPath
to a non development value, guaranteeing duplicate content is returned from wwwroot-custom
rather than wwwroot
:
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
// Examine Hosting environment: logging value
EnvironmentName = Environments.Staging,
WebRootPath = "wwwroot-custom"
});
var app = builder.Build();
app.Logger.LogInformation("ASPNETCORE_ENVIRONMENT: {env}",
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"));
app.Logger.LogInformation("app.Environment.IsDevelopment(): {env}",
app.Environment.IsDevelopment().ToString());
app.UseDefaultFiles();
app.MapStaticAssets();
app.Run();
Additional resources
ASP.NET Core