Response Compression Middleware
By Luke Latham
Network bandwidth is a limited resource. If you can reduce response payload sizes and thus send less data to clients, you can usually increase the responsiveness of your application, sometimes dramatically. One way to reduce payload sizes is to compress an application's responses.
When to use Response Compression Middleware
Use Response Compression Middleware when you're unable to use the Dynamic Compression module in IIS, the Apache mod_deflate module, NGINX Compression and Decompression, or your application is hosted directly on WebListener server or Kestrel. The main reason to use the server-based response compression technologies in IIS, Apache, or Nginx is that the performance of the middleware probably won't match that of the server modules.
Response compression
Usually, any response not natively compressed can benefit from response compression. Responses not natively compressed typically include: CSS, JavaScript, HTML, XML, and JSON. You shouldn't compress natively compressed assets, such as PNG files. If you attempt to further compress a natively compressed response, any small additional reduction in size and transmission time will likely be overshadowed by the time it took to process the compression. You also shouldn't compress files smaller than about 150-1000 bytes (depends on the file's content and the efficiency of compression), as doing so may produce a compressed file larger than the file itself.
When a client can process compressed content, the client must inform the server of its capabilities by sending the Accept-Encoding
header with the request. When a server sends compressed content, it must include information in the Content-Encoding
header on how the compressed response is encoded. Content encoding designations are shown below indicating which ones are supported by the middleware.
Accept-Encoding header values |
Middleware Supported | Description |
---|---|---|
br |
No | Brotli Compressed Data Format |
compress |
No | UNIX "compress" data format |
deflate |
No | "deflate" compressed data inside the "zlib" data format |
exi |
No | W3C Efficient XML Interchange |
gzip |
Yes (default) | Gzip file format |
identity |
Yes | "No encoding" identifier: The response must not be encoded. |
pack200-gzip |
No | Network Transfer Format for Java Archives |
* |
Yes | Any available content encoding not explicitly requested |
For more information, see the IANA Official Content Coding List.
The middleware will allow you to add additional compression providers for custom Accept-Encoding
header values. For more information, see Custom Providers below.
The middleware is capable of reacting to quality value (qvalue, q
) weighting when sent by the client to prioritize compression schemes. For more information, see RFC 7231: Accept-Encoding.
Compression algorithms are subject to a tradeoff between compression speed and the effectiveness of the compression. Effectiveness in this context refers to the size of the output after compression. The smallest size is achieved by the most optimal compression. The Gzip compression provider defaults to the fastest compression level, which might not produce the most efficient compression. If the most efficient compression is desired, the middleware can be configured for optimal compression.
The headers involved in requesting, sending, caching, and receiving compressed content are described below.
Header | Role |
---|---|
Accept-Encoding |
Sent by the client to the server to indicate which types of content encoding are acceptable. |
Content-Encoding |
Sent from the server to the client to indicate the encoding of the content in the payload. |
Content-Length |
When compression occurs, the Content-Length header is removed, since the body content changes when the response is compressed. |
Content-MD5 |
When compression occurs, the Content-MD5 header is removed, since the body content has changed and the hash is no longer valid. |
Content-Type |
Specifies the MIME type of the content. Each response should have a content-type. The middleware will check this value to determine if the response should be compressed. The middleware includes a set of default MIME types that it will encode, but you can replace or add MIME types. |
Vary |
When sent by the server with a value of Accept-Encoding to clients and proxies, it indicates that they should cache (vary) responses based on the value of the Accept-Encoding header of the request. The result of returning content with the Vary: Accept-Encoding header is that both compressed and uncompressed responses will be cached separately. |
You can explore the features of the Response Compression Middleware with the sample application. The sample illustrates the compression of application responses using Gzip and custom compression providers. It also shows you how to add a MIME type to the default list of MIME types for compression.
Package
To include the middleware in your project, add a reference to the Microsoft.AspNetCore.ResponseCompression
package. The middleware depends on .NET Framework 4.5.1 or .NET Standard 1.3 or later. This feature is available for apps that target ASP.NET Core 1.1.0 or later.
Configuration
The following highlighted code shows how to enable the Response Compression Middleware with the with the default Gzip compression and for default MIME types.
public void ConfigureServices(IServiceCollection services)
{
services.AddResponseCompression();
}
public void Configure(IApplicationBuilder app)
{
app.UseResponseCompression();
app.Run(async context =>
{
// If the Accept-Encoding header is present, always add the Vary header
// This will be added as a feature in the next release of the middleware.
// https://github.com/aspnet/BasicMiddleware/issues/187
var accept = context.Request.Headers[HeaderNames.AcceptEncoding];
if (!StringValues.IsNullOrEmpty(accept))
{
context.Response.Headers.Append(HeaderNames.Vary, HeaderNames.AcceptEncoding);
}
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync(LoremIpsum.Text);
});
}
备注
Use a tool like Fiddler, Firebug, or Postman to set the Accept-Encoding
request header and study the response headers, size, and body.
Submit a request to the sample application without the Accept-Encoding
header and observe that the response is uncompressed. The Content-Encoding
and Vary
headers are not present on the response.
Submit a request to the sample application with the Accept-Encoding: gzip
header and observe that the response is compressed. The Content-Encoding
and Vary
headers are present on the response.
Providers
GzipCompressionProvider
Use the GzipCompressionProvider
to compress responses with Gzip. This is the default compression provider if none are specified. You can set the compression level with the GzipCompressionProviderOptions
. The default is CompressionLevel.Fastest
.
Compression Level | Description |
---|---|
CompressionLevel.Fastest |
Compression should complete as quickly as possible, even if the resulting output is not optimally compressed. |
CompressionLevel.NoCompression |
No compression should be performed. |
CompressionLevel.Optimal |
Responses should be optimally compressed, even if the compression takes more time to complete. |
services.AddResponseCompression(options =>
{
options.Providers.Add<GzipCompressionProvider>();
options.Providers.Add<CustomCompressionProvider>();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "image/svg+xml" });
});
services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Fastest;
});
MIME types
The middleware specifies a default set of MIME types for compression:
text/plain
text/css
application/javascript
text/html
application/xml
text/xml
application/json
text/json
You can replace or append MIME types with the Response Compression Middleware options. Note that wildcard MIME types, such as text/*
are not supported. The sample application adds a MIME type for image/svg+xml
and will compress and serve the ASP.NET Core banner image (banner.svg).
services.AddResponseCompression(options =>
{
options.Providers.Add<GzipCompressionProvider>();
options.Providers.Add<CustomCompressionProvider>();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "image/svg+xml" });
});
Custom providers
You can create custom compression implementations with ICompressionProvider
. The EncodingName
represents the content encoding that this ICompressionProvider
produces. The middleware will use this information to choose the provider based on the list specified in the Accept-Encoding
header of the request.
Using the sample application, the client would submit a request with the Accept-Encoding: mycustomcompression
header. The middleware will use the custom compression implementation and return the response with a Content-Encoding: mycustomcompression
header. The client must be able to decompress the custom encoding in order for a custom compression implementation to work.
services.AddResponseCompression(options =>
{
options.Providers.Add<GzipCompressionProvider>();
options.Providers.Add<CustomCompressionProvider>();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "image/svg+xml" });
});
public class CustomCompressionProvider : ICompressionProvider
{
public string EncodingName => "mycustomcompression";
public bool SupportsFlush => true;
public Stream CreateStream(Stream outputStream)
{
// Create a custom compression stream wrapper here
return outputStream;
}
}
Submit a request to the sample application with the Accept-Encoding: mycustomcompression
header and observe the response headers. The Vary
and Content-Encoding
headers are present on the response. The response body (not shown) isn't compressed by the sample, as there is no compression implementation in the CustomCompressionProvider
class of the sample. However, the sample shows where you would implement such a compression algorithm.
Compression with secure protocol
Compressed responses over secure connections can be controlled with the EnableForHttps
option, which is disabled by default. Using compression with dynamically generated pages can lead to security problems such as the CRIME and BREACH attacks.
Adding the Vary header
When compressing responses based on the Accept-Encoding
header, there are potentially multiple compressed versions of the response and an uncompressed version. In order to instruct client and proxy caches that multiple versions exist and should be stored, you should supply a Vary
header with an Accept-Encoding
value. The sample application adds a Vary
header using a method; however, the middleware will be upgraded soon to provide this feature (Basic Middleware #187).
private void ManageVaryHeader(HttpContext context)
{
// If the Accept-Encoding header is present, always add the Vary header
// This will be added as a feature in the next release of the middleware.
// https://github.com/aspnet/BasicMiddleware/issues/187
var accept = context.Request.Headers[HeaderNames.AcceptEncoding];
if (!StringValues.IsNullOrEmpty(accept))
{
context.Response.Headers.Append(HeaderNames.Vary, HeaderNames.AcceptEncoding);
}
}
Middlware issue when behind an Nginx reverse-proxy
When a request is proxied by Nginx, the Accept-Encoding
header is removed. This prevents the middleware from compressing the response. For more information, see NGINX: Compression and Decompression. This issue is tracked by Figure out pass-through compression for nginx (BasicMiddleware #123).
Working with IIS dynamic compression
If you have an active IIS Dynamic Compression Module configured at the server level that you would like to disable for an application, you can do so with an addition to your web.config file. For more information, see Disabling IIS modules.
Troubleshooting
Use a tool like Fiddler, Firebug, or Postman, which allow you to set the Accept-Encoding
request header and study the response headers, size, and body. The Response Compression Middleware will compress responses that meet the following conditions:
- The
Accept-Encoding
header is present with a value ofgzip
,*
, or custom encoding that matches a custom compression provider that you've established. The value must not beidentity
or have a quality value (qvalue,q
) setting of 0 (zero). - The MIME type (
Content-Type
) must be set and must match a MIME type configured on theResponseCompressionOptions
. - The request must not include the
Content-Range
header. - The request must use insecure protocol (http), unless secure protocol (https) is configured in the Response Compression Middleware options. Note the danger described above when enabling secure content compression.