ASP.NET Core  

Benchmarking HTTP Compression Algorithms for ASP.NET Core APIs

Modern APIs rarely move small amounts of data.

A single response can contain thousands of JSON objects, large metadata structures, search results, configuration documents, or generated reports. Sending that data uncompressed increases bandwidth consumption and can increase network latency.

HTTP compression addresses this by reducing the size of response payloads before they travel across the network.

In ASP.NET Core, response compression can be enabled with middleware:

builder.Services.AddResponseCompression();

var app = builder.Build();

app.UseResponseCompression();

The interesting question is not whether compression works.

The real question is:

Which compression algorithm provides the best balance between response size, CPU consumption, latency, and throughput for a particular API?

Brotli and Gzip can produce very different results depending on the payload, compression level, CPU capacity, and network conditions.

This makes HTTP compression an excellent candidate for benchmarking rather than configuration by assumption.

Why HTTP Compression Matters

Consider an API returning a 500 KB JSON response.

Without compression:

Application
    ↓
500 KB
    ↓
Network
    ↓
Client

With compression:

Application
    ↓
500 KB JSON
    ↓
Compressed
    ↓
150 KB
    ↓
Network
    ↓
Client
    ↓
Decompressed

The server performs additional CPU work, but the network transfers substantially less data.

That creates a fundamental trade-off:

Compression
    ↓
Smaller payload
    ↓
Less network transfer

But

Compression
    ↓
More CPU work

The optimal configuration depends on which resource is the bottleneck.

Compression Algorithms in ASP.NET Core

ASP.NET Core response compression supports common HTTP compression formats including:

  • Brotli

  • Gzip

Clients communicate their supported algorithms using the Accept-Encoding header.

For example:

GET /api/products HTTP/1.1
Accept-Encoding: br, gzip

The server can then select an appropriate encoding.

The response contains:

Content-Encoding: br

or:

Content-Encoding: gzip

If compression is not appropriate, the server can return the response without compression.

Configure Brotli and Gzip

A typical ASP.NET Core configuration can enable both providers:

using Microsoft.AspNetCore.ResponseCompression;

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

    options.Providers.Add<BrotliCompressionProvider>();
    options.Providers.Add<GzipCompressionProvider>();
});

Compression levels can also be configured:

builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
{
    options.Level = System.IO.Compression.CompressionLevel.Fastest;
});

builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
    options.Level = System.IO.Compression.CompressionLevel.Fastest;
});

The exact compression level should be selected through measurement.

Faster compression is not automatically better, and maximum compression is not automatically better either.

Compression Level Creates Another Trade-Off

Compression algorithms generally provide different levels of CPU usage and compression ratio.

Conceptually:

Fastest
  ↓
More CPU efficiency
  ↓
Larger response

Balanced
  ↓
Moderate CPU
  ↓
Moderate response size

Smallest
  ↓
More CPU
  ↓
Smaller response

For a CPU-bound API, an aggressive compression level may hurt throughput.

For a bandwidth-constrained service, the additional CPU cost may be worthwhile.

What Should Be Benchmarked?

A useful benchmark should measure at least four dimensions:

  1. Compressed payload size

  2. Compression CPU time

  3. End-to-end latency

  4. Requests per second

You can represent the experiment as:

Payload
   ↓
Algorithm
   ↓
Compression
   ↓
Response
   ↓
Measure
 ┌───────────────┐
 │ Size          │
 │ CPU           │
 │ Latency       │
 │ Throughput    │
 └───────────────┘

Do not optimize only for payload size.

A 5% smaller response is not necessarily valuable if it requires significantly more CPU.

Use Realistic API Payloads

A benchmark using:

"hello world"

does not tell you much about a production API.

Use representative responses such as:

{
  "items": [
    {
      "id": 1001,
      "name": "Product A",
      "description": "Product description...",
      "category": "Technology",
      "metadata": {
        "region": "US",
        "status": "active"
      }
    }
  ]
}

Large JSON documents are particularly useful because real APIs often contain repeated property names and similar structural patterns.

Also include payloads with different characteristics.

Highly Repetitive JSON

Large compression opportunity

Random or High-Entropy Data

Limited compression opportunity

Large Text

Usually highly compressible

Already Compressed Binary Data

Often little benefit

This prevents the benchmark from producing conclusions that apply only to one type of response.

Build a Compression Benchmark Dataset

A practical test suite might contain:

DatasetApprox. SizeContent
Small JSON5 KBAPI metadata
Medium JSON100 KBSearch results
Large JSON1 MBReporting API
Large text1 MBDocumentation
Binary1 MBImage/file data

This allows the benchmark to answer a more useful question:

How does compression behave across the payload sizes my API actually serves?

Measure Compression Ratio

One of the simplest metrics is compression ratio:

Compression Ratio =
Compressed Size / Original Size

For example:

Original:   500 KB
Compressed: 150 KB

Ratio = 150 / 500
      = 0.30

That means the compressed response is approximately 30% of the original size.

You can also report bandwidth reduction:

Bandwidth Reduction =
1 - (Compressed Size / Original Size)

In this example:

1 - 0.30
= 70%

This is often easier to communicate to infrastructure teams.

Measure Compression CPU Time

Compression consumes CPU.

A benchmark should therefore record the time required to generate the compressed payload.

For example:

Algorithm     Payload     Compression Time
Brotli         1 MB          ...
Gzip           1 MB          ...

The actual numbers depend on:

  • CPU architecture

  • Runtime version

  • Compression level

  • Payload characteristics

  • Operating system

  • Memory pressure

Never treat benchmark results from one machine as universal.

Benchmark Brotli and Gzip Separately

A useful experiment can use a reusable payload:

private static readonly byte[] Payload =
    Encoding.UTF8.GetBytes(LargeJsonPayload);

Then benchmark each algorithm independently.

The benchmark should ensure that the payload and execution environment remain identical.

Conceptually:

Same payload
    |
    +---- Brotli
    |
    +---- Gzip

The only variable should be the compression configuration.

Benchmark Different Compression Levels

For Brotli, test multiple levels rather than selecting one level and declaring it optimal.

For example:

var options = new BrotliCompressionProviderOptions
{
    Level = CompressionLevel.Fastest
};

Then compare the appropriate supported levels for your workload.

The experiment should produce something similar to:

AlgorithmLevelSizeCPULatency
BrotliFast.........
BrotliBalanced.........
BrotliSmallest.........
GzipFast.........
GzipBalanced.........

The point is not to find a universally best setting.

It is to identify the best point on your application's performance curve.

Benchmark the Complete ASP.NET Core Pipeline

A pure compression benchmark is useful, but it does not represent an entire API request.

A real request includes:

HTTP Request
    ↓
Routing
    ↓
Authentication
    ↓
Controller/Endpoint
    ↓
Serialization
    ↓
Compression
    ↓
Network

For realistic results, benchmark the complete API.

For example:

app.MapGet("/api/products", () =>
{
    return Results.Ok(GetLargeProductCollection());
});

Then invoke the endpoint with:

Accept-Encoding: br

and:

Accept-Encoding: gzip

This captures serialization and middleware overhead in addition to compression.

Compression and JSON Serialization

Compression happens after the application produces the response representation.

That means serialization performance still matters.

Consider:

Database
   ↓
Objects
   ↓
JSON Serialization
   ↓
Compression
   ↓
HTTP Response

If JSON serialization takes 20 ms and compression takes 2 ms, reducing compression to 1 ms may not materially improve total latency.

Similarly, if compression takes 15 ms on a large response, optimization may be worthwhile.

Always measure the entire request path.

Test HTTPS Traffic

Compression is frequently used over HTTPS.

ASP.NET Core allows response compression to be enabled for HTTPS:

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

However, enabling compression indiscriminately requires careful security consideration.

Compression should primarily target appropriate response content and should not be treated as a universal setting for every response.

Avoid Compressing Everything

Some payloads do not benefit significantly from HTTP compression.

Examples include:

  • JPEG images

  • PNG images

  • MP4 video

  • ZIP archives

  • GZIP files

  • Other already-compressed formats

Compressing them again can consume CPU while providing little size reduction.

A useful rule is:

Already compressed?
      ↓
Usually skip HTTP compression

Content Type Matters

Compression is most useful for text-based content such as:

  • JSON

  • XML

  • HTML

  • CSS

  • JavaScript

  • Plain text

ASP.NET Core response compression uses MIME types to determine which responses should be compressed.

You can configure additional MIME types when required:

builder.Services.AddResponseCompression(options =>
{
    options.MimeTypes = new[]
    {
        "application/json",
        "application/problem+json",
        "text/plain"
    };
});

Do not simply add every content type.

Verify that the selected data actually benefits from compression.

Small Responses May Not Be Worth Compressing

Compression has overhead.

For a response of only a few hundred bytes:

Compression CPU
+
Compression metadata

may not justify the small bandwidth savings.

A useful benchmark should therefore include small responses.

For example:

1 KB
10 KB
100 KB
1 MB
5 MB

You may find that compression becomes increasingly valuable as payload size increases.

Network Conditions Change the Result

CPU benchmarks alone can produce the wrong conclusion.

Imagine:

Brotli:
Smaller payload
Higher CPU

and:

Gzip:
Larger payload
Lower CPU

On a fast internal network, Gzip may provide better overall latency.

On a slow mobile connection, Brotli may win because transferring fewer bytes matters more.

Therefore, test under representative network conditions.

Calculate End-to-End Latency

A useful simplified model is:

Total Latency =
Application Processing
+
Serialization
+
Compression
+
Network Transfer
+
Client Processing

Compression changes two parts of this equation:

Compression
    ↓
CPU time increases

Payload size
    ↓
Network transfer decreases

The correct algorithm is the one that improves the total result for your workload.

Benchmark Throughput

A high-traffic API also needs throughput testing.

Measure:

Requests per second

while maintaining a realistic concurrency level.

For example:

10 concurrent requests
50 concurrent requests
100 concurrent requests
500 concurrent requests

Watch for:

  • CPU saturation

  • Memory growth

  • Increased latency

  • Garbage collection

  • Queueing

  • Reduced throughput

An algorithm that performs well for one request may behave differently under sustained load.

Watch CPU Utilization

Compression can become CPU-bound under high traffic.

Suppose:

Without compression
CPU: 45%

With compression
CPU: 82%

The bandwidth savings may be valuable, but the application has much less CPU headroom.

This matters when the service is already close to its scaling threshold.

Compression can therefore affect infrastructure costs as well as application performance.

Use Response Size as a Scaling Metric

A useful operational metric is total compressed bytes sent.

For example:

Requests/sec:       1,000
Average response:   200 KB

Without compression:

≈ 200 MB/sec

If compression reduces the average response to 60 KB:

≈ 60 MB/sec

That difference can materially affect network utilization.

The exact numbers should always be calculated from your actual traffic.

Common Mistakes

Benchmarking Only Compression Ratio

A smaller response does not automatically mean a faster API.

Using Tiny Test Payloads

Compression behavior becomes more meaningful with realistic response sizes.

Ignoring CPU

Compression trades network bandwidth for CPU work.

Compressing Already-Compressed Data

This can waste CPU without meaningful bandwidth savings.

Testing Only One Compression Level

Different levels can produce different CPU/size trade-offs.

Ignoring Network Conditions

A compression strategy that works well on a datacenter network may behave differently for geographically distributed clients.

Benchmarking Only the Compression Library

The API also spends time on routing, business logic, serialization, and other middleware.

Best Practices

  1. Benchmark Brotli and Gzip using identical payloads.

  2. Test multiple payload sizes.

  3. Measure response size and compression ratio.

  4. Measure CPU and memory consumption.

  5. Include end-to-end API latency.

  6. Test multiple compression levels.

  7. Measure throughput under realistic concurrency.

  8. Avoid compressing already-compressed formats.

  9. Enable compression only for content types that benefit.

  10. Test representative network conditions.

  11. Compare compression overhead against serialization and application processing time.

  12. Re-run benchmarks after major .NET runtime upgrades.

  13. Monitor CPU and network utilization in production.

  14. Select compression settings based on workload rather than generic recommendations.

Frequently Asked Questions

Is Brotli always better than Gzip?

No. Brotli can provide strong compression for many text workloads, but the best choice depends on payload, compression level, CPU capacity, and network conditions.

Does compression always improve API latency?

No. Compression adds CPU work. If the network is already fast and the response is small, compression may provide little benefit.

Should JSON APIs always enable compression?

Large JSON responses are often good candidates, but the decision should be based on measurements and response size.

Should images be compressed by ASP.NET Core?

Usually not when they are already encoded in compressed formats such as JPEG or PNG. Recompressing them often provides little additional benefit.

What is the most important benchmark metric?

There is no single metric. The useful combination is compressed size, CPU cost, end-to-end latency, and throughput.

Should production use the highest compression level?

Not automatically. Higher compression can increase CPU consumption. Benchmark the workload and choose the level that provides an acceptable size/CPU trade-off.

Conclusion

HTTP compression is a classic example of an optimization that involves a trade-off rather than a simple yes-or-no decision.

The fundamental relationship is:

More Compression
      ↓
Smaller Payload
      ↓
Less Network Traffic

But

More Compression
      ↓
More CPU Work

For ASP.NET Core APIs, Brotli and Gzip should therefore be evaluated against realistic workloads rather than selected purely from generic benchmarks.

A useful experiment should measure:

Payload Size
      ↓
Compression Algorithm
      ↓
Compression Level
      ↓
CPU
      ↓
Latency
      ↓
Throughput
      ↓
Network Transfer

The best configuration is the one that improves the overall behavior of the application.

For a bandwidth-constrained API, stronger compression may be worth additional CPU usage. For a CPU-bound internal service, a faster compression level may be the better choice.

The important engineering principle is simple: HTTP compression should be treated as a measurable performance trade-off, not a default optimization that can be evaluated using payload size alone.