|
| 1 | +--- |
| 2 | +title: Map static files in ASP.NET Core |
| 3 | +author: rick-anderson |
| 4 | +description: Learn how to serve and secure mapped static files and configure static file hosting middleware behaviors in an ASP.NET Core web app. |
| 5 | +monikerRange: '>= aspnetcore-8.0' |
| 6 | +ms.author: riande |
| 7 | +ms.custom: mvc |
| 8 | +ms.date: 3/18/2025 |
| 9 | +uid: fundamentals/map-static-files |
| 10 | +--- |
| 11 | + |
| 12 | +# Map static files in ASP.NET Core |
| 13 | + |
| 14 | +:::moniker range=">= aspnetcore-9.0" |
| 15 | + |
| 16 | +By [Rick Anderson](https://twitter.com/RickAndMSFT) |
| 17 | + |
| 18 | +Static files, such as HTML, CSS, images, and JavaScript, are assets an ASP.NET Core app serves directly to clients by default. |
| 19 | + |
| 20 | +For Blazor static files guidance, which adds to or supersedes the guidance in this article, see <xref:blazor/fundamentals/static-files>. |
| 21 | + |
| 22 | +## Serve static files |
| 23 | + |
| 24 | +Static files are stored within the project's [web root](xref:fundamentals/index#web-root) directory. The default directory is `{content root}/wwwroot`, but it can be changed with the <xref:Microsoft.AspNetCore.Hosting.HostingAbstractionsWebHostBuilderExtensions.UseWebRoot%2A> method. For more information, see [Content root](xref:fundamentals/index#content-root) and [Web root](xref:fundamentals/index#web-root). |
| 25 | + |
| 26 | +The <xref:Microsoft.AspNetCore.Builder.WebApplication.CreateBuilder%2A> method sets the content root to the current directory: |
| 27 | + |
| 28 | +[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet&highlight=1,15)] |
| 29 | + |
| 30 | +Static files are accessible via a path relative to the [web root](xref:fundamentals/index#web-root). For example, the **Web Application** project templates contain several folders within the `wwwroot` folder: |
| 31 | + |
| 32 | +* `wwwroot` |
| 33 | + * `css` |
| 34 | + * `js` |
| 35 | + * `lib` |
| 36 | + |
| 37 | +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` |
| 38 | + |
| 39 | +### MapStaticAssets |
| 40 | + |
| 41 | +Creating performant web apps requires optimizing asset delivery to the browser. Possible optimizations include: |
| 42 | + |
| 43 | +* Serve a given asset once until the file changes or the browser clears its cache. Set the [ETag](https://developer.mozilla.org/docs/Web/HTTP/Headers/ETag) header. |
| 44 | +* Prevent the browser from using old or stale assets after an app is updated. Set the [Last-Modified](https://developer.mozilla.org/docs/Web/HTTP/Headers/Last-Modified) header. |
| 45 | +* Set up proper [caching headers](https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control). |
| 46 | +* Use [caching middleware](xref:performance/caching/middleware). |
| 47 | +* Serve [compressed](/aspnet/core/performance/response-compression) versions of the assets when possible. |
| 48 | +* Use a [CDN](/microsoft-365/enterprise/content-delivery-networks?view=o365-worldwide&preserve-view=true) to serve the assets closer to the user. |
| 49 | +* Minimize the size of assets served to the browser. This optimization doesn't include minification. |
| 50 | + |
| 51 | +<xref:Microsoft.AspNetCore.Builder.StaticAssetsEndpointRouteBuilderExtensions.MapStaticAssets%2A>: |
| 52 | +* 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. |
| 53 | +* 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. |
| 54 | + |
| 55 | +### `MapStaticAssets` versus `UseStaticFiles` |
| 56 | + |
| 57 | +<xref:Microsoft.AspNetCore.Builder.StaticAssetsEndpointRouteBuilderExtensions.MapStaticAssets%2A> is available in ASP.NET Core in .NET 9.0 and later. <xref:Microsoft.AspNetCore.Builder.StaticFileExtensions.UseStaticFiles%2A> must be used in versions prior to .NET 9.0. |
| 58 | + |
| 59 | +<xref:Microsoft.AspNetCore.Builder.StaticFileExtensions.UseStaticFiles%2A> 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. |
| 60 | + |
| 61 | +The following features are supported in `UseStaticFiles` but not in `MapStaticAssets`: |
| 62 | + |
| 63 | +* [Serving files from disk or embedded resources, or other locations](xref:fundamentals/static-files#serve-files-from-multiple-locations) |
| 64 | +* [Serve files outside of web root](xref:fundamentals/static-files#serve-files-outside-of-web-root) |
| 65 | +* [Set HTTP response headers](xref:fundamentals/static-files#set-http-response-headers) |
| 66 | +* [Directory browsing](xref:fundamentals/static-files#directory-browsing) |
| 67 | +* [Serve default documents](xref:fundamentals/static-files#serve-default-documents) |
| 68 | +* [`FileExtensionContentTypeProvider`](xref:fundamentals/static-files#fileextensioncontenttypeprovider) |
| 69 | +* [Serve files from multiple locations](xref:fundamentals/static-files#serve-files-from-multiple-locations) |
| 70 | +* [Serving files from disk or embedded resources, or other locations](xref:fundamentals/static-files#serve-files-from-multiple-locations) |
| 71 | +* [Serve files outside of web root](xref:fundamentals/static-files#serve-files-outside-of-web-root) |
| 72 | +* [Set HTTP response headers](xref:fundamentals/static-files#set-http-response-headers) |
| 73 | +* [Directory browsing](xref:fundamentals/static-files#directory-browsing) |
| 74 | +* [Serve default documents](xref:fundamentals/static-files#serve-default-documents) |
| 75 | +* [`FileExtensionContentTypeProvider`](xref:fundamentals/static-files#fileextensioncontenttypeprovider) |
| 76 | +* [Serve files from multiple locations](xref:fundamentals/static-files#serve-files-from-multiple-locations) |
| 77 | + |
| 78 | +### Serve files in web root |
| 79 | + |
| 80 | +The default web app templates call the <xref:Microsoft.AspNetCore.Builder.StaticAssetsEndpointRouteBuilderExtensions.MapStaticAssets%2A> method in `Program.cs`, which enables static files to be served: |
| 81 | + |
| 82 | +[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet&highlight=15)] |
| 83 | + |
| 84 | +The parameterless `MapStaticAssets` method overload marks the files in [web root](xref:fundamentals/index#web-root) as servable. The following markup references `wwwroot/images/MyImage.jpg`: |
| 85 | + |
| 86 | +```html |
| 87 | +<img src="~/images/MyImage.jpg" class="img" alt="My image" /> |
| 88 | +``` |
| 89 | + |
| 90 | +In the preceding markup, the tilde character `~` points to the [web root](xref:fundamentals/index#web-root). |
| 91 | + |
| 92 | +### Serve files outside of web root |
| 93 | + |
| 94 | +Consider a directory hierarchy in which the static files to be served reside outside of the [web root](xref:fundamentals/index#web-root): |
| 95 | + |
| 96 | +* `wwwroot` |
| 97 | + * `css` |
| 98 | + * `images` |
| 99 | + * `js` |
| 100 | +* `MyStaticFiles` |
| 101 | + * `images` |
| 102 | + * `red-rose.jpg` |
| 103 | + |
| 104 | +A request can access the `red-rose.jpg` file by configuring the Static File Middleware as follows: |
| 105 | + |
| 106 | +[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_rr&highlight=1,18-23)] |
| 107 | + |
| 108 | +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. |
| 109 | + |
| 110 | +The following markup references `MyStaticFiles/images/red-rose.jpg`: |
| 111 | +<!-- zz test via /Home2/MyStaticFilesRR --> |
| 112 | +[!code-html[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Views/Home2/MyStaticFilesRR.cshtml?range=1)] |
| 113 | + |
| 114 | +To serve files from multiple locations, see [Serve files from multiple locations](xref:fundamentals/static-files#serve-files-from-multiple-locations). |
| 115 | + |
| 116 | +### Set HTTP response headers |
| 117 | + |
| 118 | +A <xref:Microsoft.AspNetCore.Builder.StaticFileOptions> object can be used to set HTTP response headers. In addition to configuring static file serving from the [web root](xref:fundamentals/index#web-root), the following code sets the [Cache-Control](https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control) header: |
| 119 | + |
| 120 | +[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_rh&highlight=16-24)] |
| 121 | + |
| 122 | +The preceding code makes static files publicly available in the local cache for one week. |
| 123 | + |
| 124 | +## Static file authorization |
| 125 | + |
| 126 | +The ASP.NET Core templates call <xref:Microsoft.AspNetCore.Builder.StaticAssetsEndpointRouteBuilderExtensions.MapStaticAssets%2A> before calling <xref:Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.UseAuthorization%2A>. Most apps follow this pattern. When `MapStaticAssets` is called before the authorization middleware: |
| 127 | + |
| 128 | + * No authorization checks are performed on the static files. |
| 129 | + * Static files served by the Static File Middleware, such as those under `wwwroot`, are publicly accessible. |
| 130 | + |
| 131 | +To serve static files based on authorization, see [Static file authorization](xref:fundamentals/static-files#static-file-authorization). |
| 132 | + |
| 133 | +## Serve files from multiple locations |
| 134 | + |
| 135 | +Consider the following Razor page which displays the `/MyStaticFiles/image3.png` file: |
| 136 | + |
| 137 | +[!code-cshtml[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Pages/Test.cshtml?highlight=5)] |
| 138 | + |
| 139 | +`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`: |
| 140 | + |
| 141 | +[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_mul)] |
| 142 | + |
| 143 | +Using the preceding code: |
| 144 | + |
| 145 | +* The `/MyStaticFiles/image3.png` file is displayed. |
| 146 | +* The [Image Tag Helpers](xref:mvc/views/tag-helpers/builtin-th/image-tag-helper) <xref:Microsoft.AspNetCore.Mvc.TagHelpers.ImageTagHelper.AppendVersion> is not applied because the Tag Helpers depend on <xref:Microsoft.AspNetCore.Hosting.IWebHostEnvironment.WebRootFileProvider>. `WebRootFileProvider` has not been updated to include the `MyStaticFiles` folder. |
| 147 | + |
| 148 | +The following code updates the `WebRootFileProvider`, which enables the Image Tag Helper to provide a version: |
| 149 | + |
| 150 | +[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_mult2)] |
| 151 | + |
| 152 | +> [!NOTE] |
| 153 | +> The preceding approach applies to Razor Pages and MVC apps. For guidance that applies to Blazor Web Apps, see <xref:blazor/fundamentals/static-files#serve-files-from-multiple-locations>. |
| 154 | +
|
| 155 | +## Serve files outside wwwroot by updating IWebHostEnvironment.WebRootPath |
| 156 | + |
| 157 | +When <xref:Microsoft.AspNetCore.Hosting.IWebHostEnvironment.WebRootPath%2A?displayProperty=nameWithType> is set to a folder other than `wwwroot`: |
| 158 | + |
| 159 | +* In the development environment, static assets found in both `wwwroot` and the updated `IWebHostEnvironment.WebRootPath` are served from `wwwroot`. |
| 160 | +* In any environment other than development, duplicate static assets are served from the updated `IWebHostEnvironment.WebRootPath` folder. |
| 161 | + |
| 162 | +Consider a web app created with the empty web template: |
| 163 | + |
| 164 | +* Containing an `Index.html` file in `wwwroot` and `wwwroot-custom`. |
| 165 | +* With the following updated `Program.cs` file that sets `WebRootPath = "wwwroot-custom"`: |
| 166 | + |
| 167 | + [!code-csharp[](~/fundamentals/static-files/samples/9.x/WebRoot/Program.cs?name=snippet1)] |
| 168 | + |
| 169 | +In the preceding code, requests to `/`: |
| 170 | + |
| 171 | +* In the development environment return `wwwroot/Index.html` |
| 172 | +* In any environment other than development return `wwwroot-custom/Index.html` |
| 173 | + |
| 174 | +To ensure assets from `wwwroot-custom` are returned, use one of the following approaches: |
| 175 | + |
| 176 | +* Delete duplicate named assets in `wwwroot`. |
| 177 | +* Set `"ASPNETCORE_ENVIRONMENT"` in `Properties/launchSettings.json` to any value other than `"Development"`. |
| 178 | +* Completely disable static web assets by setting `<StaticWebAssetsEnabled>false</StaticWebAssetsEnabled>` in the project file. ***WARNING, disabling static web assets disables [Razor Class Libraries](xref:razor-pages/ui-class)***. |
| 179 | +* Add the following XML to the project file: |
| 180 | + |
| 181 | + ```xml |
| 182 | + <ItemGroup> |
| 183 | + <Content Remove="wwwroot\**" /> |
| 184 | + </ItemGroup> |
| 185 | + ``` |
| 186 | + |
| 187 | +The following code updates `IWebHostEnvironment.WebRootPath` to a non development value, guaranteeing duplicate content is returned from `wwwroot-custom` rather than `wwwroot`: |
| 188 | + |
| 189 | +[!code-csharp[](~/fundamentals/static-files/samples/9.x/WebRoot/Program.cs?name=snippet2&highlight=5)] |
| 190 | + |
| 191 | +## Additional resources |
| 192 | + |
| 193 | +* [View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/fundamentals/static-files/samples) ([how to download](xref:index#how-to-download-a-sample)) |
| 194 | +* [Middleware](xref:fundamentals/middleware/index) |
| 195 | +* [Introduction to ASP.NET Core](xref:index) |
| 196 | +* <xref:blazor/file-uploads> |
| 197 | +* <xref:blazor/file-downloads> |
| 198 | + |
| 199 | +:::moniker-end |
0 commit comments