GZip Compression in ASP.NET Core

Response compression is one of the easiest performance wins you can implement in your APIs. By compressing responses before sending them over the network, you can significantly reduce bandwidth usage and improve response times.

In this article, we'll explore GZip compression in ASP.NET Core APIs: why you should use it, how to implement it correctly, and how to handle edge cases like OpenAPI documentation endpoints.

Why Use Response Compression?

Network bandwidth is a limited and often expensive resource. Compressing API responses provides several benefits:

  • Reduced bandwidth: Compressed JSON responses are typically 60-90% smaller
  • Faster response times: Smaller payloads transfer faster, especially on slower networks
  • Lower costs: Less data transfer means reduced cloud egress costs
  • Better mobile experience: Mobile users on limited data plans benefit significantly

When Compression Makes Sense

Compression works best for:

  • JSON/XML responses: Text-based formats compress extremely well
  • Large payloads: Responses larger than 1KB benefit most from compression
  • Read-heavy APIs: GET endpoints returning lists or complex objects

When to Avoid Compression

Don't compress:

  • Already compressed formats: Images (PNG, JPEG), videos, or ZIP files
  • Small responses: Files under 150-1000 bytes may become larger after compression
  • Real-time streaming: SSE or WebSocket connections may prefer uncompressed data
  • CPU-constrained environments: Compression adds CPU overhead

How to Enable Response Compression

ASP.NET Core provides built-in middleware for response compression. Here's how to set it up:

Basic Configuration

var builder = WebApplication.CreateBuilder(args);

// Add response compression services
builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
});

var app = builder.Build();

// Enable the middleware - must be before other middleware that writes responses
app.UseResponseCompression();

app.MapGet("/api/products", () => GetProducts());

app.Run();

By default, ASP.NET Core includes both Brotli and GZip compression providers. The middleware automatically selects the best algorithm based on the client's Accept-Encoding header.

Advanced Configuration with Custom Settings

For more control, you can configure compression providers and levels:

using System.IO.Compression;
using Microsoft.AspNetCore.ResponseCompression;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    options.Providers.Add<BrotliCompressionProvider>();
    options.Providers.Add<GzipCompressionProvider>();
    
    // Add additional MIME types to compress
    options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat([
        "application/json",
        "application/xml",
        "text/csv"
    ]);
});

// Configure compression levels
builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
{
    options.Level = CompressionLevel.Fastest;
});

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

var app = builder.Build();

app.UseResponseCompression();

Understanding Compression Levels

The CompressionLevel enum offers three options:

Level Description Use Case
Fastest Minimal compression, lowest CPU usage High-throughput APIs, real-time responses
Optimal Balanced compression and CPU usage General-purpose APIs
SmallestSize Maximum compression, highest CPU usage Bandwidth-constrained scenarios

How It Works Under the Hood

When a client sends a request with an Accept-Encoding header:

GET /api/products HTTP/1.1
Host: api.example.com
Accept-Encoding: gzip, deflate, br

The server responds with a compressed body and a Content-Encoding header:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Encoding: br
Vary: Accept-Encoding

[compressed response body]

The Vary: Accept-Encoding header tells caches to store different versions based on the encoding.

Excluding OpenAPI/Scalar from Compression

When using OpenAPI documentation tools like Scalar or Swagger UI, response compression can cause issues. These tools often need to read the raw OpenAPI specification, and compression can interfere with their JavaScript-based rendering.

The Problem

If you enable response compression globally, the OpenAPI JSON specification and the Scalar/Swagger UI pages may not render correctly. The browser may fail to decompress the response properly, or the JavaScript may receive garbled data.

Solution 1: Exclude MIME Types

You can exclude specific MIME types from compression using ExcludedMimeTypes:

builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    
    // Exclude OpenAPI-related MIME types from compression
    options.ExcludedMimeTypes = [
        "application/json;charset=utf-8", // Sometimes used by OpenAPI
        "text/html"                        // Scalar/Swagger UI pages
    ];
});

However, this approach also excludes your API JSON responses, which defeats the purpose.

Solution 2: Conditional Middleware with MapWhen

A better approach is to skip compression for specific paths:

var app = builder.Build();

// Apply compression only to non-OpenAPI paths
app.UseWhen(
    context => !context.Request.Path.StartsWithSegments("/openapi") &&
               !context.Request.Path.StartsWithSegments("/scalar"),
    appBuilder => appBuilder.UseResponseCompression()
);

// Map OpenAPI and Scalar endpoints
app.MapOpenApi();
app.MapScalarApiReference();

app.MapGet("/api/products", () => GetProducts());

app.Run();

Security Considerations

Enabling compression over HTTPS comes with security implications. The CRIME and BREACH attacks can exploit compression to leak sensitive data.

Mitigating BREACH Attacks

  1. Use CSRF tokens: ASP.NET Core's antiforgery tokens help protect against BREACH
  2. Avoid reflecting user input: Don't include user-controlled data in compressed responses alongside secrets
  3. Randomize padding: Add random data to responses to mask compression ratios

For most APIs that don't serve HTML with secrets, the risk is minimal. However, always evaluate your specific use case.

builder.Services.AddResponseCompression(options =>
{
    // Only enable for HTTPS after evaluating security implications
    options.EnableForHttps = true;
});

Testing Compression

Use browser developer tools or curl to verify compression is working:

# Request with gzip encoding
curl -H "Accept-Encoding: gzip" -I https://localhost:5001/api/products

# Response should include:
# Content-Encoding: gzip
# Vary: Accept-Encoding

Or use the --compressed flag:

curl --compressed https://localhost:5001/api/products

Performance Benchmarks

Here's a typical comparison for a JSON API response:

Scenario Response Size Transfer Time (100ms latency)
Uncompressed 50 KB ~500ms
GZip (Fastest) 8 KB ~100ms
Brotli (Fastest) 6 KB ~80ms

Actual results vary based on data patterns and network conditions, but compression typically provides 5-10x size reduction for JSON payloads.

Complete Example

Here's a complete Program.cs showing all the concepts together:

using System.IO.Compression;
using Microsoft.AspNetCore.ResponseCompression;

var builder = WebApplication.CreateBuilder(args);

// Configure response compression
builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    options.Providers.Add<BrotliCompressionProvider>();
    options.Providers.Add<GzipCompressionProvider>();
});

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

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

// Add OpenAPI services
builder.Services.AddOpenApi();

var app = builder.Build();

// Map OpenAPI endpoints before compression middleware
app.MapOpenApi();
app.MapScalarApiReference();

// Enable compression for API endpoints
app.UseResponseCompression();

// Your API endpoints
app.MapGet("/api/products", () => 
{
    return Enumerable.Range(1, 100).Select(i => new 
    {
        Id = i,
        Name = $"Product {i}",
        Description = "A detailed product description that benefits from compression"
    });
});

app.Run();

Key Takeaways

  1. Enable compression for JSON APIs: It's one of the easiest performance wins, reducing payload sizes by 60-90%

  2. Use the built-in middleware: ASP.NET Core's AddResponseCompression handles Brotli and GZip automatically

  3. Consider compression levels: Use Fastest for high-throughput scenarios, SmallestSize when bandwidth is the bottleneck

  4. Evaluate HTTPS security implications: For most APIs, the benefits outweigh the risks, but be aware of BREACH attacks for sensitive HTML content

Response compression is a low-effort, high-impact optimization that every API should consider. With just a few lines of code, you can significantly improve your API's performance and reduce infrastructure costs.

Additional Resources