MNW #044: Response Compression In ASP.NET Core

[删除(380066935@qq.com或微信通知)]

更好的阅读体验请查看原文:https://www.milanjovanovic.tech/blog/response-compression-in-aspnetcore

A big thank you to our sponsors who keep this newsletter free to the reader:

Today's issue is sponsored by IcePanel. IcePanel realigns your engineering & product teammates on the technical decisions made across the business. Explain your complex software systems with an interactive map that teammates can drill down to the level of detail they need.

And by Treblle. Treblle is a lightweight SDK that helps engineering and product teams build, ship & maintain REST-based APIs faster. Simple integration for all popular languages & frameworks, including .NET 6.


Reducing the size of your API responses can noticeably improve the performance of your application.

And since network bandwidth is a limited resource, you should at least consider the benefits of response compression.

Here's what you'll learn in this week's newsletter:

  • How to configure response compression in .NET
  • Server-based vs. application-based compression
  • Possible security risks and mitigation strategies
  • How to configure the available compression providers
  • How much network bandwidth you could be saving

Let's get started.

Configuring Response Compression

Using response compression in an .NET applications is remarkably easy.

You only have to call these two methods:

  • AddResponseCompression - to configure the default services for response compression
  • UseResponseCompression - to add the response compression middleware to the request pipeline

The UseResponseCompression method should be called before any middleware that compresses responses.

Response compression isn't turned on by default for HTTPS, so you have to enable it by setting EnableForHttps to true.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
});

var app = builder.Build();

app.UseResponseCompression();

app.MapGet("/", () => "This response will be compressed 📦");

app.Run();

It really is that simple.

But...

When Should You Use Response Compression?

Ideally, you want to be using server-based response compression if your application server supports it.

Because the middleware performs response compression at the application level, it will typically have worse performance.

If you are hosting your application and you can't use server-based compression, then using the response compression middleware is justified.

One more concern should be security, because using response compression over HTTPS can expose you to CRIME and BREACH attacks

Here's what you can do to improve security:

  • You can mitigate CRIME and BREACH attacks by introducing anti-forgery tokens in ASP.NET Core
  • Don't send application secrets as part of the request body
  • Implement a rate limiter

Configuring Compression Providers

There are two compression providers added by default when you call AddResponseCompression:

  • BrotliCompressionProvider
  • GzipCompressionProvider

You can further customize the available providers by adding custom compression providers if you want to.

Compression will default to Brotli compression when it's supported by the client. Otherwise, it will default to Gzip if that is the supported compression format.

builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    options.Providers.Add<BrotliCompressionProvider>();
    options.Providers.Add<GzipCompressionProvider>();
    options.MimeTypes = ResponseCompressionDefaults.MimeTypes;
});

The interesting thing is you can configure the CompressionLevel for the Brotli and Gzip compression providers.

There are four possible values:

  • Optimal - tries to balance response size and compression speed
  • Fastest - sacrifices optimal compression for improved speed
  • NoCompression - self explanatory
  • SmallestSize - sacrifices compression speed to make the output as small as possible

The default value is CompressionLevel.Fastest.

builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
{
    options.Level = CompressionLevel.Optimal;
});

builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
    options.Level = CompressionLevel.SmallestSize;
});

How Much Can You Save?

Let's find out how much network bandwidth we can save by using response compression.

Here's a minimal API endpoint returning a list of Message objects:

app.MapGet("/", () => Results.Ok(
    Enumerable
        .Range(1, 100)
        .Select(num => new Message
        {
            Id = num,
            Content = $"This is the message #{num}"
        })));

And here are the results with different providers and compression levels:

  • No compression - 4.5kB
  • Gzip + CompressionLevel.Fastest - 569B
  • Gzip + CompressionLevel.SmallestSize - 539B
  • Gzip + CompressionLevel.Optimal - 554B
  • Brotli + CompressionLevel.Fastest - 400B
  • Brotli + CompressionLevel.SmallestSize - 296B
  • Brotli + CompressionLevel.Optimal - 319B

Brotli is the clear winner, which is why it's the default compression provider.

In the best case scenario, you can reduce the response size by ~93.5%. Multiply this by the number of requests you're serving daily, and then you can begin to estimate the possible network savings.

One more thing I noticed is that using CompressionLevel.SmallestSize has a noticeable negative impact on response time. I can't say this was surprising, so I suggest to simply keep using the default compression level.

In Summary

Response compression is an interesting technique to improve API performance and reduce network costs.

Ideally, you'd want to be using server-based response compression if it's supported by your application server. If that's not the case, application-based compression is available in .NET with the response compression middleware.

What's the cost of response compression?

It will increase the CPU load, and can expose some security risks over HTTPS, but there are ways to mitigate this.

In my experience, the default configuration values for the compression provider and compression level give excellent results.

That's all for this week.

See you next Saturday.

Today's action step: To see the value of response compression, I suggest enabling it in your application and examining the changes in response size. You can try the different compression providers by varying the Accept-Encoding header, and also configure different compression levels in your application.