Response Compression in ASP.NET Core: A Beginner's Guide
Learn how response compression works in ASP.NET Core. Shrink Gzip and Brotli responses, speed up your API, and avoid common pitfalls.
Imagine you are packing clothes for a long train journey across India. If you just throw everything loose into your bag, it fills up fast and barely closes. But if you roll each shirt tightly and use a vacuum bag to squeeze the air out, the same clothes take half the space. The clothes are the same when you unpack them. You just sent them in a smaller, tighter bundle.
Response compression in ASP.NET Core does exactly this for your web pages and API data. The server squeezes the response into a smaller bundle before sending it over the internet. The browser unpacks it on the other side. The user sees the same page, but it arrives faster and uses less data. On a slow mobile connection, this can feel like magic.
In this guide we will learn what compression is, how ASP.NET Core does it, how to switch it on, and the mistakes to avoid. We will keep it simple and friendly. By the end you will be able to add compression to your own app with confidence.
Why compression matters
Every time someone opens your website, the server sends data over the network. Text data like HTML, CSS, JavaScript and JSON is very "repetitive". The same words and patterns appear again and again. Compression spots these repeats and writes them in a shorter way.
Smaller responses mean three good things:
- Pages load faster, especially on slow networks.
- Users spend less mobile data, which they care about a lot.
- Your server and bandwidth bills can go down.
Text files often shrink by 70 to 90 percent. That is a huge win for almost no effort.
How the browser and server agree
Compression is a polite conversation. The browser does not force the server. They agree on a format first. This handshake uses HTTP headers.
When a browser asks for a page, it sends a header that lists the formats it understands:
Accept-Encoding: gzip, brHere gzip means Gzip and br means Brotli. These are two popular squeezing methods. The server reads this list, picks one it also supports, compresses the response, and adds a header to say what it used:
Content-Encoding: brNow the browser knows it received a Brotli bundle and unpacks it automatically. The user never sees any of this. It just works.
Gzip vs Brotli
ASP.NET Core ships with two built-in compression providers: Gzip and Brotli. Both come ready to use. You do not install anything extra for them.
Brotli is newer and usually squeezes text smaller than Gzip. Almost every modern browser supports it. Gzip is older but supported everywhere, including old clients. ASP.NET Core is smart here. If the browser supports Brotli, it uses Brotli. If not, it falls back to Gzip. You get the best of both without extra work.
| Provider | Strength | Browser support | Default choice |
|---|---|---|---|
Brotli (br) | Smaller output for text | All modern browsers | Used first when supported |
Gzip (gzip) | Reliable and universal | Every browser, even old ones | Used as a fallback |
Think of Brotli as the vacuum bag and Gzip as a sturdy rolled bundle. The vacuum bag is tighter, but the rolled bundle always works.
Turning compression on
Let us add compression to a real app. There are two simple steps. First you register the service. Then you tell the middleware pipeline to use it. Here is a minimal Program.cs for .NET 10.
var builder = WebApplication.CreateBuilder(args);
// Step 1: register response compression
builder.Services.AddResponseCompression(options =>
{
// also compress over HTTPS (read the security note below first)
options.EnableForHttps = true;
});
var app = builder.Build();
// Step 2: use it early in the pipeline
app.UseResponseCompression();
app.MapGet("/", () => "Hello from a compressed response!");
app.Run();That is the whole thing. By default this gives you both Brotli and Gzip. The order matters a lot, which we will cover next.
Middleware order is important
ASP.NET Core handles a request by passing it through a line of middleware, one after another. The order is like an assembly line in a factory. Each station does its job and passes the work along.
Compression must run before anything that writes the response body. If you put it too late, the data is already sent and there is nothing left to squeeze. A good rule: call app.UseResponseCompression() near the top, before static files and before your endpoints.
Where compression sits in the pipeline
Steps
Request
Browser asks for data
Compression
Wraps the output stream
Routing
Finds the handler
Endpoint
Builds the body
Response
Squeezed bundle leaves
If you get the order wrong, you will simply see no compression. There is no crash and no error message. That makes this a sneaky bug. So always double-check the order if compression is not working.
Choosing a compression level
Squeezing harder makes the bundle smaller but uses more CPU time. ASP.NET Core lets you pick how hard to squeeze. There are three levels.
| Level | What it does | When to use |
|---|---|---|
Fastest | Light squeeze, low CPU | Server CPU is busy |
Optimal | Balanced squeeze | Good default for most apps |
SmallestSize | Hardest squeeze, more CPU | Bandwidth matters most |
By default the providers use Fastest. For most websites Optimal is a nice middle ground. Here is how you set the level for both providers.
using System.IO.Compression;
using Microsoft.AspNetCore.ResponseCompression;
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
});
// tune each provider's effort
builder.Services.Configure<BrotliCompressionProviderOptions>(o =>
{
o.Level = CompressionLevel.Optimal;
});
builder.Services.Configure<GzipCompressionProviderOptions>(o =>
{
o.Level = CompressionLevel.Optimal;
});Test your own app before deciding. Measure the response size and the CPU use. The right level depends on your traffic and your server.
Which content types get compressed
ASP.NET Core does not compress everything. It only squeezes a set of "safe" content types like HTML, CSS, JavaScript, JSON and XML. This is on purpose. Some files should never be compressed.
Images such as JPEG and PNG, video files such as MP4, and archives such as ZIP are already compressed. Squeezing them again wastes CPU and can make them slightly bigger. So the middleware leaves them alone.
If your API sends a custom content type, you can add it to the list. For example, to compress SVG images, which are really text:
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "image/svg+xml" });
});Be careful here. Only add text-like types. Adding image/png would just waste effort.
The security note: CRIME and BREACH
You may have noticed EnableForHttps is turned off by default. There is a real reason. Two attacks called CRIME and BREACH can sometimes guess secret data by watching how the compressed size changes. This works best on HTTPS pages that mix secret tokens with attacker-controlled input.
ASP.NET Core plays safe. It keeps compression off for HTTPS until you turn it on yourself. For most apps that serve public content, turning it on is fine. If your responses contain secrets like anti-forgery tokens next to user input, be more careful. Anti-forgery tokens and not reflecting user input into compressed secret responses help reduce the risk.
The short, beginner-friendly advice: turn on EnableForHttps for normal public pages and static assets, and think twice before compressing pages that mix secrets with user input.
Deciding on HTTPS compression
Steps
Public content?
Marketing, blog, static files
Secrets mixed in?
Tokens plus user input
Enable
Safe to compress
Be cautious
Review before enabling
Should the server or a proxy do it?
There is one more thing to know. Compression does not have to happen inside your ASP.NET Core app. A web server or reverse proxy in front of your app, such as Nginx, IIS or Apache, can do it instead. These servers are often faster at it and free up your app's CPU.
So when should you use the built-in middleware? Use it when no proxy is doing the job, or when you are hosting somewhere simple like a single container. If you already run Nginx or IIS in front and it compresses responses, you usually do not need the middleware too. Pick one place to compress, not both. Compressing twice is wasted work.
A good plan for many teams: let the reverse proxy handle compression in production, and use the middleware during local development or for simple hosting setups.
A quick test to prove it works
After you wire everything up, you want to confirm it actually compresses. The easiest way is your browser's developer tools. Open the Network tab, reload the page, click a request, and look at the response headers. You should see Content-Encoding: br or Content-Encoding: gzip. You should also see the transferred size is smaller than the actual size.
You can also test from the command line. Sending an Accept-Encoding header tells the server you accept compression, and the response headers will show what it used.
// A tiny endpoint that returns a big repetitive string,
// perfect for seeing compression shrink it a lot.
app.MapGet("/big", () =>
{
var text = string.Concat(Enumerable.Repeat("hello world ", 5000));
return Results.Text(text);
});Hit this endpoint with and without compression and compare the transferred bytes. The difference is usually dramatic for repetitive text like this.
A real-world example: a JSON API
Let us picture a common case. You build an API that returns a list of products as JSON. Each product has a name, a price, a description and a few tags. With a hundred products, the JSON might be around 80 KB of text. JSON is full of repeated keys like "name" and "price" on every item, so it compresses beautifully.
After turning on compression, that same response might travel as only 10 to 15 KB over the wire. The browser still receives the full 80 KB after unpacking, so your front-end code does not change at all. Nothing in your GET /products endpoint or your GET /products/{id} endpoint needs editing. The squeezing happens automatically inside the middleware. This is why compression is such a popular, low-effort win: you change a couple of lines in Program.cs and every text response across your whole app gets smaller.
One more friendly tip: compression and caching work well together. If you also cache responses, the cached copy can be stored already compressed, so repeat visitors get a fast, small response without the server squeezing it again. The two features are good teammates.
Common mistakes to avoid
A few traps catch beginners. Here are the ones to watch for.
- Calling
UseResponseCompressiontoo late in the pipeline, so nothing gets squeezed. - Forgetting
AddResponseCompression, so the middleware has no service to use. - Trying to compress images or videos, which only wastes CPU.
- Compressing both in Nginx and in the app, doing the work twice.
- Expecting tiny responses to shrink. Very small bodies may not be worth compressing, and the savings are small.
Keep these in mind and your setup will be smooth.
Quick recap
- Response compression squeezes text responses so they travel faster and use less data, like a vacuum bag for your data.
- The browser and server agree on a format using the
Accept-EncodingandContent-Encodingheaders. - ASP.NET Core supports Brotli and Gzip out of the box, and prefers Brotli when the browser supports it.
- Switch it on with
AddResponseCompressionplusUseResponseCompression, and place the middleware early in the pipeline. - Pick a level:
Fastest,Optimal, orSmallestSize.Optimalis a friendly default. - Only compress text-like content. Leave images, video and archives alone.
EnableForHttpsis off by default because of CRIME and BREACH risks. Turn it on for normal public content.- A reverse proxy like Nginx or IIS can compress instead of your app, and is often faster. Compress in one place, not two.
References and further reading
- Response compression in ASP.NET Core (Microsoft Learn)
- AspNetCore.Docs source for response compression (GitHub)
- Response Compression in ASP.NET Core (Milan Jovanovic)
- ASP.NET Core compress Gzip Brotli content encoding (Gunnar Peipman)
Related Posts
How to Increase the Performance of Web APIs in .NET
A friendly, step-by-step guide to making your ASP.NET Core Web APIs fast: async, caching, query tuning, compression, and pooling in .NET 10.
ASP.NET Core Output Cache: Speed Up Your API with In-Memory and Redis
Learn ASP.NET Core output caching the easy way: cache whole API responses in memory or in Redis, set policies, vary by query, and clear with tags — with diagrams and code.
How to Implement Caching Strategies in .NET: A Beginner-Friendly Guide
Learn caching strategies in .NET with simple examples: in-memory cache, distributed Redis cache, and HybridCache, plus eviction, stampede protection, and tags.
HybridCache in ASP.NET Core: The New Caching Library Explained
Learn HybridCache in ASP.NET Core the simple way: two-level caching, stampede protection, tag invalidation, GetOrCreateAsync, and Redis setup with clear diagrams.
Master Claims Transformation for Flexible ASP.NET Core Authorization
Learn claims transformation in ASP.NET Core to enrich the user identity and build flexible, policy-based authorization with IClaimsTransformation.
Make Your ASP.NET Core Web API 18x Faster with HybridCache
Learn how HybridCache in .NET 9 speeds up your ASP.NET Core Web API up to 18x, with L1/L2 caching, stampede protection, and tags, explained simply.