.NET  

Benchmarking HTTP Request Compression in .NET 11

HTTP payload size matters more than it first appears. Large JSON requests consume bandwidth, increase transfer time, and can become expensive when applications communicate across regions, mobile networks, or high-latency connections.

Compression can reduce the amount of data transmitted, but it is not free. The sender has to spend CPU time compressing the payload, and the receiver has to spend CPU time decompressing it. The right compression strategy therefore depends on the workload rather than simply choosing the algorithm with the highest compression ratio.

.NET has continued expanding its compression capabilities across the runtime and ASP.NET Core. Recent .NET 11 development includes additional compression-related improvements, making it a useful platform for evaluating request-compression trade-offs. Earlier .NET releases already provided request decompression support in ASP.NET Core, where compressed request bodies can be transparently decompressed by middleware.

This article focuses on the engineering question behind the feature: when does HTTP request compression actually improve an application's behavior, and when does the CPU cost outweigh the network savings?

Why HTTP Request Compression Matters

Consider an API that accepts a large JSON document:

{
  "customerId": 10245,
  "orders": [
    {
      "productId": 1001,
      "quantity": 5,
      "description": "A detailed product description..."
    }
  ]
}

A small request may not benefit much from compression. For a large request containing repetitive JSON property names, strings, arrays, and other structured data, compression can substantially reduce the number of bytes transmitted.

The trade-off can be summarized as:

FactorWithout CompressionWith Compression
Network payloadLargerSmaller
Upload bandwidthHigherLower
Sender CPULowerHigher
Receiver CPULowerHigher
Compression latencyNoneAdditional
Best fitSmall/simple payloadsLarge/repetitive payloads
Main riskNetwork costCPU overhead

The important point is that compression is an optimization across the entire request path, not just a serialization feature.

HTTP Request Compression vs Response Compression

These concepts are easy to confuse.

Response compression compresses data produced by the server before it is sent to the client.

Request compression compresses data produced by the client before it is uploaded to the server.

For example:

Client
  |
  | Compressed HTTP Request
  v
ASP.NET Core API
  |
  | Decompress
  v
Application

ASP.NET Core has supported request decompression middleware for clients that send compressed request bodies. When the Content-Encoding header identifies a supported encoding, the middleware wraps the request body in the appropriate decompression stream. Decompression occurs when the request body is read rather than eagerly processing the entire body up front.

That distinction matters when designing benchmarks because measuring only server-side request processing does not tell you the complete cost of compression.

Compression Algorithms to Evaluate

A meaningful benchmark should compare algorithms using the same payloads and request conditions.

Common compression formats available in the .NET ecosystem include:

  • Gzip

  • Deflate

  • Brotli

  • Zstandard

The .NET APIs expose these through the compression libraries and related HTTP functionality. The DecompressionMethods API, for example, includes GZip, Deflate, Brotli, and Zstandard in current .NET API documentation.

Each algorithm represents a different balance between compression speed and resulting payload size.

Brotli is a useful example because compression quality can be tuned. Higher compression levels can reduce output size further but require more processing. Microsoft has previously demonstrated that compression-level selection can produce very different CPU costs, reinforcing why a benchmark should measure both size and execution time rather than declaring one algorithm universally superior.

Designing a Useful Benchmark

A benchmark should answer more than:

"Which algorithm produces the smallest file?"

Instead, measure at least these dimensions:

  1. Original payload size

  2. Compressed payload size

  3. Compression ratio

  4. Compression time

  5. Decompression time

  6. End-to-end request latency

  7. Requests per second

  8. CPU utilization

  9. Memory allocation

A simple compression-ratio calculation is:

Compression Ratio = Compressed Size / Original Size

For example, if a 10 MB payload becomes 2 MB:

2 MB / 10 MB = 0.20

The compressed representation is 20% of the original payload.

That does not automatically mean the application is faster. If compression adds enough CPU work to dominate the request, the smaller network payload may not compensate for it.

Building a Benchmark Payload

Use representative data rather than artificially repetitive strings.

For example, create a moderately large JSON document:

public sealed record Order(
    int Id,
    string Product,
    int Quantity,
    decimal Price);

public sealed record OrderBatch(
    int CustomerId,
    List<Order> Orders);

Generate multiple records:

var orders = Enumerable.Range(1, 50_000)
    .Select(i => new Order(
        i,
        $"Product-{i % 500}",
        (i % 10) + 1,
        10 + (i % 100)))
    .ToList();

var payload = new OrderBatch(
    10001,
    orders);

Serialize it using System.Text.Json:

var json = JsonSerializer.Serialize(payload);
var data = Encoding.UTF8.GetBytes(json);

Console.WriteLine($"Payload size: {data.Length:N0} bytes");

This gives the benchmark a deterministic starting point.

For a serious evaluation, create several payload classes instead of relying on one dataset:

PayloadPurpose
Small JSONDetermine whether compression overhead dominates
Medium JSONRepresent typical API requests
Large JSONMeasure bandwidth savings
Highly repetitive JSONTest compression efficiency
Random-like JSONTest less-compressible data
Binary contentDetermine whether compression is useful at all

Measuring Compression with BenchmarkDotNet

BenchmarkDotNet is a practical choice for measuring compression overhead in isolation.

A simplified benchmark can look like this:

[MemoryDiagnoser]
public class CompressionBenchmark
{
    private byte[] _payload = null!;

    [GlobalSetup]
    public void Setup()
    {
        _payload = File.ReadAllBytes("payload.json");
    }

    [Benchmark]
    public byte[] Gzip()
    {
        using var output = new MemoryStream();

        using (var gzip = new GZipStream(
            output,
            CompressionMode.Compress,
            leaveOpen: true))
        {
            gzip.Write(_payload);
        }

        return output.ToArray();
    }

    [Benchmark]
    public byte[] Brotli()
    {
        using var output = new MemoryStream();

        using (var brotli = new BrotliStream(
            output,
            CompressionMode.Compress,
            leaveOpen: true))
        {
            brotli.Write(_payload);
        }

        return output.ToArray();
    }
}

The important detail is that the payload should be prepared during GlobalSetup. Otherwise, file I/O or payload generation can contaminate the measurement.

The benchmark should also avoid allocating unnecessary objects inside the measured operation unless those allocations are intentionally part of what you want to measure.

Measuring HTTP End-to-End Behavior

A compression microbenchmark is useful, but it does not represent a real HTTP request.

For an end-to-end test, create an ASP.NET Core API that accepts a request body and enable request decompression.

A simplified server configuration is:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRequestDecompression();

var app = builder.Build();

app.UseRequestDecompression();

app.MapPost("/orders", async (HttpRequest request) =>
{
    using var reader = new StreamReader(request.Body);

    var json = await reader.ReadToEndAsync();

    return Results.Ok(new
    {
        ReceivedBytes = json.Length
    });
});

app.Run();

The middleware allows the application to work with the decompressed request body rather than forcing each endpoint to implement decompression manually.

The client should send the appropriate Content-Encoding header so the server knows how the body has been encoded.

The benchmark should then compare:

Scenario A: Uncompressed request
Scenario B: Gzip request
Scenario C: Brotli request
Scenario D: Other supported compression format

The key measurement is not simply server execution time. Measure the complete request lifecycle.

Measuring the Network Trade-Off

A useful production-oriented model is:

Total Request Cost =
    Serialization
  + Compression
  + Network Transfer
  + Decompression
  + Server Processing

Without compression:

Serialization
     +
Network Transfer
     +
Server Processing

With compression:

Serialization
     +
Compression
     +
Smaller Network Transfer
     +
Decompression
     +
Server Processing

Compression wins when the reduction in transfer cost is greater than the additional compression and decompression overhead.

This is why results can differ dramatically between environments.

A service communicating over a fast internal network may gain little from compression. The same service communicating between geographically distant regions or over constrained connections may benefit significantly.

HTTP Version Should Be Kept Consistent

When comparing compression strategies, do not accidentally change other networking variables.

HTTP/3, for example, uses QUIC rather than TCP and has different transport characteristics from HTTP/1.1 and HTTP/2.

Therefore, if the goal is specifically to benchmark compression, keep the HTTP version consistent across test scenarios.

Otherwise, the experiment can unintentionally measure multiple variables simultaneously.

For example:

Test A
HTTP/2 + Gzip

Test B
HTTP/3 + Brotli

A difference between those tests cannot reliably be attributed to compression alone.

Production Considerations

Choose Compression Based on Payload Size

Do not blindly compress every request.

For very small payloads, compression metadata and CPU work may provide little benefit.

A practical policy can be based on request size:

if (payload.Length < MinimumCompressionSize)
{
    // Send without compression
}
else
{
    // Compress request
}

The threshold should be determined through measurement rather than copied from another application.

Avoid Compressing Already-Compressed Data

Formats such as JPEG, PNG, MP4, ZIP, and many archive formats are already compressed.

Running another compression algorithm over such data can consume CPU while producing little size reduction.

Consider CPU Capacity

Compression shifts some work from the network to the CPU.

That trade can be desirable when bandwidth is constrained but undesirable when the application is already CPU-bound.

Monitor Both Sides

Client-side CPU and server-side CPU should both be measured.

The client performs compression:

Client CPU ↑
Network bytes ↓

The server performs decompression:

Network bytes ↓
Server CPU ↑

The optimization therefore needs to be evaluated across the complete system.

Common Benchmarking Mistakes

Benchmarking Only Compression Ratio

A smaller payload is not automatically a faster request.

Always measure compression time and end-to-end latency.

Using Only One Payload

Compression behavior depends heavily on the data.

Benchmark multiple payload sizes and structures.

Including Setup Work in the Measurement

Generating JSON, reading files, or creating random test data inside the benchmark can distort results.

Prepare test data before the measured operation.

Comparing Different Network Conditions

Running one test locally and another across a remote environment makes the results difficult to interpret.

Keep network topology, server hardware, HTTP version, and concurrency consistent.

Treating Preview Results as Production Benchmarks

.NET 11 preview APIs and behavior can change before final release.

A benchmark conducted against a preview should clearly identify the runtime build and test environment. It should be repeated against the final release before making production decisions.

Troubleshooting

Compressed Requests Are Rejected

Check the Content-Encoding header.

The server must know which encoding was used, and the corresponding decompression support must be configured.

Compression Reduces Bandwidth but Increases Latency

Measure CPU time.

If compression is expensive relative to the network savings, reduce compression levels, change algorithms, or apply compression only above a suitable payload threshold.

Results Change Between Runs

Check:

  • CPU frequency scaling

  • Background processes

  • Garbage collection

  • Network variability

  • Connection reuse

  • Payload generation

  • Concurrent request count

Run multiple iterations and use statistical summaries rather than relying on one request.

DNS or Connection Behavior Changes Results

Reuse HttpClient appropriately and control connection lifetime. HttpClient maintains connection pools, and Microsoft's guidance notes that connection lifetime can affect DNS refresh behavior and connection creation overhead.

Recommended Benchmark Matrix

For a serious engineering evaluation, use a matrix like this:

VariableValues
Payload sizeSmall / Medium / Large
Payload typeJSON / Text / Binary
CompressionNone / Gzip / Brotli / Zstandard where supported
Compression levelFast / Balanced / Maximum
HTTP versionHTTP/1.1 / HTTP/2 / HTTP/3
Concurrency1 / 10 / 100 / Higher workload-specific levels
NetworkLocal / Latency-injected / Representative production path
MetricsSize / CPU / Memory / Latency / Throughput

The most useful result is not a single winner. It is a decision boundary showing which compression strategy works best for which workload.

Best Practices

  1. Benchmark representative production payloads.

  2. Measure both compressed size and CPU cost.

  3. Include end-to-end network latency.

  4. Test multiple payload sizes.

  5. Keep HTTP version and connection behavior consistent.

  6. Avoid compressing already-compressed formats.

  7. Establish a minimum payload threshold.

  8. Monitor client and server CPU.

  9. Record the exact .NET runtime version and environment.

  10. Revalidate preview benchmarks against the final runtime before production adoption.

Frequently Asked Questions

Does HTTP request compression always improve API performance?

No. Compression reduces network traffic but introduces CPU and processing overhead. It is most useful when network transfer is a meaningful part of total request cost.

Should every JSON request be compressed?

Not necessarily. Small JSON payloads may not justify the additional processing. Benchmark different payload sizes and establish an application-specific threshold.

Is Brotli always better than Gzip?

No. The appropriate choice depends on payload characteristics, compression settings, CPU capacity, and network conditions. Benchmark both using the workload your application actually handles.

Does request compression replace response compression?

No. They solve different problems. Request compression reduces client-to-server traffic, while response compression reduces server-to-client traffic.

Should compression be benchmarked separately from HTTP?

Both measurements are useful. Microbenchmarks isolate compression overhead, while end-to-end HTTP tests show whether those savings translate into better application behavior.

Conclusion

HTTP request compression is fundamentally a systems trade-off.

Reducing payload size can lower bandwidth consumption and network transfer time, but compression introduces additional CPU work on the client and decompression work on the server. The correct decision therefore cannot be based solely on compression ratio.

.NET 11 provides an interesting platform for evaluating these trade-offs as its networking and compression capabilities continue to evolve. The most valuable benchmark is one that measures the entire request path: payload size, compression cost, network transfer, decompression, latency, throughput, CPU, and memory.

For production systems, the goal should not be to find the universally "best" compression algorithm. The goal is to identify where compression provides a measurable net benefit for your specific workload.

That is the benchmark result that can actually guide an engineering decision.