ASP.NET Core response compression

ASP.NET Core supports response compression. From popular algorithms gzip and Brotli are supported. Those who like can also implement their own response compression providers. This blog post shows how to response compression works in ASP.NET Core.

With ASP.NET Core we have three options for compression:

  1. ASP.NET Core does compression
  2. Front-end web server does compression
  3. Static files are precompressed

We can skip second and third option here because we are interested in how ASP.NET Core carries out response compression. Those who don’t use Microsoft.AspNetCore.App metapackage must add reference to Microsoft.AspNetCore.ResponseCompression NuGet package to enable response compression.

Test page

I’m using simple test page for this guide. Not much content, not many files and effects of compression are easy to see and compare.

By default, without any compression page loads like shown on image below.

Response with no compression

When read from cache the numbers are different.

Response with cache

I start with a table to visualize effects of response compression better.

CompressionResponse size (KB)%
No compression764.99100.00
No compression, from cache11.251.47

Let’s leave these numbers here for a moment and let’s make compression work.

gzip

Let’s get started with gzip compression as it comes out-of-box. No tricks, no miracles. First we have to set option for compression and then add compression to request middleware pipeline. We do it in ConfigureServices() method of Startup class.

services.Configure<GzipCompressionProviderOptions>(options =>
{
     options.Level = CompressionLevel.Optimal;
});
 
services.AddResponseCompression(options =>
{
     options.EnableForHttps = true;
     options.Providers.Add<GzipCompressionProvider>();
});

For HTTPS we have to say when we want to use compression due to CRIME and BREACH security exploits.

There are three possible values for compression level:

  • NoCompression – compression is ignored
  • Optimal – golden way between compression level and CPU resources (default)
  • Fastest – compressing with minimal effects on server load

Next we have to modify Configure() method of Startup class.

app.UseStaticFiles();
 
// Enable compression
app.UseResponseCompression();
 
app.UseMvc(routes =>
{
     routes.MapRoute(
         name: "default",
         template: "{controller=Home}/{action=Index}/{id?}");
});

BUG!!! I made one bad mistake in code above but let’s get to it later as it doesn’t affect what we are doing right now.

Let’s try to load the page with both compession levels. We start with fastest compression and then try optimal.

gzip with Fastest compression levelgzip with Fastest compression level

gzip with Optimal compression levelgzip with Optimal compression level

And let’s write the results to table.

CompressionResponse size (KB)%
No compression764.99100.00
No compression, from cache11.251.47
gzip, fastest652.6285.31
gzip, optimal652.4385.23

Numbers are very similar for both compression levels. It should ring the alarm bell for those readers who have worked with response compression before. But we get to it later.

Brotli

Brotli is new open-source compression algorithm supported by almost all major browsers. It provides better compression that gzip. More information about browsers support for Brotli is available at Can I Use… site.

Brotli support in modern browsers

On our application side there’s nothing very much different. We have already everything configured and introducing Brotli is easy. We have to configure its options and add Brotli compression provider to response compression providers collection.

services.Configure<BrotliCompressionProviderOptions>(options =>
{
     options.Level = CompressionLevel.Optimal;
});
 
services.AddResponseCompression(options =>
{
     options.EnableForHttps = true;     options.Providers.Add<BrotliCompressionProvider>();
});

With brotli enabled let’s make also two tests – one with each compression level.

Brotli with Fastest compression levelBrotli with Fastest compression level

Brotly with Optimal compression levelBrotly with Optimal compression level

Let’s add these results to table.

CompressionResponse size (KB)%
No compression764.99100.00
No compression, from cache11.251.47
gzip, fastest652.6285.31
gzip, optimal652.4385.23
Brotli, fastest652.7485.32
Brotli, optimal651.8385.21

Almost on the same level with gzip and it seems weird to me. Why introduce new compression algorithm if benefits are so small? Okay, time to confess…

Compressing static files

I did great on wiping some dust under carpet before and I didn’t stop on closed investigation of static files. Let’s run application and see some static file.

Static file is not compressed

Not compressed, why? The question comes down to request middleware ordering in application start-up. We have to put compression before static files in Configure() method. Otherwise static files middleware finds static file and returns it immediately.

// Enable compression
app.UseResponseCompression();
app.UseStaticFiles();
 
app.UseMvc(routes =>
{
     routes.MapRoute(
         name: "default",
         template: "{controller=Home}/{action=Index}/{id?}");
});

With static files properly managed let’s run application again to see the results.

CompressionResponse size (KB)%
No compression764.99100.00
No compression, from cache11.251.47
gzip, fastest289.9237.90
gzip, optimal268.9235.15
Brotli, fastest293.7438.40
Brotli, optimal252.6533.03

When we compare the results now we can see more difference. Brotli with optimal compression is faster and it gives even more effect when responses are bigger than I had.

Controlling compression of static files

By default, these MIME-types are compressed:

  • application/javascript
  • application/json
  • application/xml
  • text/css
  • text/html
  • text/json
  • text/plain

When configuring response compression we can specify our own list of types to compress.

services.AddResponseCompression(options =>
{
     IEnumerable<string> MimeTypes = new[]
     {
         // General
         "text/plain",
         "text/html",
         "text/css",
         "font/woff2",
         "application/javascript",
         "image/x-icon",
         "image/png"
     };

     options.EnableForHttps = true;
     options.MimeTypes = MimeTypes;
     options.Providers.Add<GzipCompressionProvider>();
     options.Providers.Add<BrotliCompressionProvider>();
});

Similar way we can exclude set of MIME-types from compression.

services.AddResponseCompression(options =>
{     IEnumerable<string> MimeTypes = new[]
     {
         // General
         "text/plain",
         "text/html",
         "text/css",
         "font/woff2",
         "application/javascript",
         "image/x-icon",
         "image/png"
     };

     options.EnableForHttps = true;
     options.ExcludedMimeTypes = MimeTypes;
     options.Providers.Add<GzipCompressionProvider>();
     options.Providers.Add<BrotliCompressionProvider>();
});

Having type if list of allowed types doesn’t mean that compression happens. When activated then compression to use is decided based on browser Accept-Encoding header. If this header doesn’t specify any known compression algorithm then ASP.NET Core doesn’t compress the requested file.

Wrapping up

Response compression can save us a lot of traffic when applied in ASP.NET Core web application. Public interface of response compression API is simple and flexible. We can control compression level and MIME-types to be compressed. For static files we have to include response compression before static files middleware because otherwise it is not run. And as we saw then new Brotli compression is better than older gzip.

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

    4 thoughts on “ASP.NET Core response compression

    • February 20, 2019 at 4:29 pm
      Permalink

      Does compressing “image/png” makes sense since png is basically a zipped bmp?

    • February 20, 2019 at 5:50 pm
      Permalink

      I have not seen much effect on compressing png or jpg. What works best for images is optimizing these with special software or online services. It’s more effective as optimization is done once and it doesn’t add any load to server later.

    • April 10, 2019 at 10:05 pm
      Permalink

      Hi Gunnar, great article!

      It would be great if you could help me:

      I have a JSON, and I am sending a byte array inside one of the JSON Properties.

      However, even when receiving the response with the Content-Encoding as gzip, the size of the payload is the same as not using the gzip compression (I am using Postman).

      Do you have any idea of what is happening?

      Cheers!

    • April 11, 2019 at 12:58 am
      Permalink

      Hi,
      Is size exactly the same or similar? If similar then I think the problem is about byte array. Usually binary data doesn’t compress very well and in some cases compressed binary can be even larger than original one.

    Leave a Reply

    Your email address will not be published. Required fields are marked *