Response Compression Middleware in ASP.NET Core
Response compression reduces the size of HTTP responses before sending them to the client, decreasing bandwidth usage and improving page load times. ASP.NET Core includes middleware that handles Brotli and Gzip compression out of the box.
When to Use It
Use the built-in response compression middleware when:
- You are hosting directly on Kestrel without a reverse proxy.
- Your reverse proxy (Nginx, IIS, Caddy) does not handle compression.
- You want fine-grained control over which content types and endpoints are compressed.
If your reverse proxy already compresses responses, enabling it again in ASP.NET Core wastes CPU cycles without additional benefit.
Basic Setup
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true; // Disabled by default for security
});
var app = builder.Build();
app.UseResponseCompression();
// Must be registered before any middleware that writes response bodies
app.UseStaticFiles();
app.MapControllers();
app.Run();
Important: UseResponseCompression() must be placed before any middleware that produces response content. If it comes after UseStaticFiles, static file responses will not be compressed.
Configuring Providers
By default, ASP.NET Core includes both Brotli and Gzip providers. The client's Accept-Encoding header determines which is used. Brotli generally achieves better compression ratios than Gzip:
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
});
builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
{
// Level 4 is a good balance between compression ratio and CPU cost
options.Level = CompressionLevel.Optimal;
});
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.SmallestSize;
});
The CompressionLevel enum offers these options:
| Level | Behaviour |
|---|---|
Fastest |
Minimal CPU usage, lower compression |
Optimal |
Balanced compression and speed |
SmallestSize |
Maximum compression, higher CPU cost |
NoCompression |
Disables compression (useful for testing) |
MIME Type Filtering
By default, the middleware compresses common text-based content types (HTML, CSS, JavaScript, JSON, XML). You can add custom types:
builder.Services.AddResponseCompression(options =>
{
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
{
"application/octet-stream",
"image/svg+xml",
"application/wasm"
});
});
Do not add already-compressed types like JPEG, PNG, or ZIP — compressing them wastes CPU without reducing their size.
The HTTPS Security Concern
You may have noticed EnableForHttps is false by default. This is because of CRIME and BREACH attacks, which exploit the relationship between compression ratio and secret data in the response. If an attacker can observe response sizes and control parts of the response content, they can potentially extract secrets like CSRF tokens.
For most API-only applications, this risk is low. For applications serving sensitive data alongside user-controlled content, evaluate the risk before enabling HTTPS compression:
builder.Services.AddResponseCompression(options =>
{
// Only enable after considering BREACH attack implications
options.EnableForHttps = true;
});
Conditional Compression
You can exclude specific endpoints from compression by setting the response header:
app.MapGet("/real-time/stream", async (HttpContext context) =>
{
// Disable compression for streaming responses
var compressionFeature = context.Features
.Get<IHttpResponseBodyFeature>();
context.Response.Headers.ContentEncoding = "identity";
// Stream data without compression
await context.Response.WriteAsync("data: event 1\n\n");
await context.Response.Body.FlushAsync();
});
For Server-Sent Events or streaming responses, compression adds latency because the middleware buffers data before compressing. In these cases, disable it for those specific endpoints.
Measuring the Impact
A typical JSON API response might compress from 15 KB to 3 KB with Brotli — an 80% reduction. To verify compression is working, check the response headers:
Content-Encoding: br // Brotli
Content-Encoding: gzip // Gzip
Vary: Accept-Encoding
The Vary header tells caches that the response varies based on Accept-Encoding, preventing a compressed response from being served to a client that does not support it.
Custom Compression Provider
If you need a different algorithm, implement ICompressionProvider:
public class ZstdCompressionProvider : ICompressionProvider
{
public string EncodingName => "zstd";
public bool SupportsFlush => true;
public Stream CreateStream(Stream outputStream)
{
return new ZstdStream(outputStream, CompressionMode.Compress);
}
}
builder.Services.AddResponseCompression(options =>
{
options.Providers.Add<ZstdCompressionProvider>();
});
Key Takeaways
Response compression is a straightforward win for reducing bandwidth. Use Brotli for the best compression ratios, place the middleware early in the pipeline, and be mindful of the HTTPS security implications. Skip compression for already-compressed content types and streaming endpoints where latency matters more than size.