ASP.NET Core middleware that automatically serves pre-compressed static files (Brotli and Gzip) when available, falling back to uncompressed files.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Add compressed static files middleware
app.UseCompressedStaticFiles();
app.Run();.NET 10+ automatically compresses static assets during publish. For earlier versions or when copying a project separately after build (such as a Vite React project), you'll need to compress files manually.
For Vite projects, use the vite-plugin-compression2 plugin:
npm install vite-plugin-compression2 --save-dev// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import compression from 'vite-plugin-compression2'
export default defineConfig({
plugins: [
react(),
// Generate both .gz and .br files
compression({
algorithms: ["gzip", "brotliCompress"],
}),
]
})The CompressedStaticFileOptions class provides the following configuration options:
| Property | Type | Description |
|---|---|---|
RequestPath |
PathString |
See SharedOptionsBase.RequestPath |
ContentTypeProvider |
IContentTypeProvider |
See StaticFileOptions.ContentTypeProvider |
DefaultContentType |
string? |
See StaticFileOptions.DefaultContentType |
ServeUnknownFileTypes |
bool |
See StaticFileOptions.ServeUnknownFileTypes |
OnPrepareResponse |
Action<StaticFileResponseContext>? |
See StaticFileOptions.OnPrepareResponse |
OnPrepareResponseAsync |
Func<StaticFileResponseContext, Task>? |
See StaticFileOptions.OnPrepareResponseAsync |
FileSystemPath |
string? |
The physical file system path to serve files from. If not specified, the web root path will be used. |
Encodings |
Dictionary<string, EncodingOptions> |
Dictionary mapping Content-Encoding values to their configuration (file extension and priority). Default includes Brotli ("br" with extension .br and priority 0) and Gzip ("gzip" with extension .gz and priority 1). |
The EncodingOptions class configures each supported encoding:
| Property | Type | Description |
|---|---|---|
Extension |
string |
The file extension for this encoding (e.g., ".br", ".gz"). |
Priority |
int |
Priority for this encoding when client quality values are equal. Lower values have higher priority. Default is 0. |
This example demonstrates serving compressed static files from a custom directory with a specific request path and cache headers:
app.UseCompressedStaticFiles(new CompressedStaticFileOptions
{
RequestPath = "/static",
FileSystemPath = Path.Combine(builder.Environment.WebRootPath, "static"),
OnPrepareResponse = ctx =>
{
// Add cache headers for long-term caching
ctx.Context.Response.Headers.CacheControl = "public,max-age=31536000,immutable";
},
});This example shows how to add support for additional compression formats or customize priorities:
app.UseCompressedStaticFiles(new CompressedStaticFileOptions
{
Encodings = new Dictionary<string, EncodingOptions>(StringComparer.OrdinalIgnoreCase)
{
// Brotli with highest priority
{ "br", new EncodingOptions { Extension = ".br", Priority = 0 } },
// Gzip with medium priority
{ "gzip", new EncodingOptions { Extension = ".gz", Priority = 1 } },
// Deflate with lowest priority (if you have .zz files)
{ "deflate", new EncodingOptions { Extension = ".zz", Priority = 2 } }
}
});This middleware is built on top of ASP.NET Core's UseStaticFiles middleware. As such, all security protections provided by ASP.NET Core are inherited, including:
- Protection against path traversal attacks (e.g.,
../sequences) - Invalid filename character validation
- Query string handling (e.g.,
/sample.txt?ab=cd) - Other built-in security features of the static files middleware
The middleware intelligently serves compressed files based on the client's Accept-Encoding header:
- Quality-based selection: The middleware parses quality values (q-values) from the
Accept-Encodingheader and prioritizes encodings with higher quality values - Priority tie-breaking: When multiple encodings have the same quality value, the middleware uses the
Priorityproperty to determine which encoding to serve first - Fallback behavior: If no compressed version exists or the client doesn't support any configured encodings, it serves the uncompressed file
- Wildcard handling: The wildcard
*inAccept-Encodingis ignored for safety, resulting in uncompressed file delivery
The middleware automatically sets the Content-Encoding header when serving compressed files.
Note: When using ASP.NET Core's
UseResponseCompressionmiddleware, it will automatically ignore any files served by this middleware that are already compressed. This prevents double compression and ensures optimal performance.
Accept-Encoding: br, gzip→ Serves Brotli (both have quality 1.0, Brotli has priority 0)Accept-Encoding: gzip;q=0.9, br;q=0.8→ Serves Gzip (higher quality value)Accept-Encoding: br;q=0→ Serves uncompressed (q=0 means not acceptable)Accept-Encoding: *→ Serves uncompressed (wildcard is ignored)
When no ContentTypeProvider is specified, the middleware creates a default FileExtensionContentTypeProvider with all configured compressed extensions removed from the mappings. This ensures that:
- Compressed files (e.g.,
script.js.br,style.css.gz) are not directly served as separate content types - The content type is determined from the original file name (e.g.,
script.js.bris served with the content type for.jsfiles) - Direct requests to compressed files (e.g.,
/script.js.br) will return 404 Not Found, preventing clients from bypassing the compression negotiation
If you provide a custom ContentTypeProvider, you should ensure it does not include mappings for your compressed file extensions.
This project is licensed under the MIT License. See the LICENSE file for details.
Glory to Jehovah, Lord of Lords and King of Kings, creator of Heaven and Earth, who through his Son Jesus Christ, has reedemed me to become a child of God. -Shane32