dotnet / dotnet/aspnetcore

Output caching should always vary by Accept-Encoding

Open
#45,998 4 comments 1 reaction 0 assignees View on GitHub
area-middleware feature-caching feature-output-caching
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Describe the bug

When both response compression and output caching are enabled, it appears that ASP.NET Core caches the first encoding to be generated, then always returns content with that encoding.

### Expected Behavior

I expected each encoding to be cached separately so responses are correct while avoiding the overhead of compressing each response.

### Steps To Reproduce

This project has a simple repro:
https://github.com/jonpayne/AspNetCacheIssue/tree/main/src

Using this repro, if I first request the text file with Brotli compression, I get the expected result:

```bash
curl -s -D - https://localhost:5000/test.txt -H "Accept-Encoding: br" -o br.dat
```

```http
HTTP/1.1 200 OK
Content-Type: text/plain
Date: Tue, 10 Jan 2023 05:33:16 GMT
Server: Kestrel
Accept-Ranges: bytes
Content-Encoding: br
ETag: "1d924af6fef9583"
Last-Modified: Tue, 10 Jan 2023 04:53:10 GMT
Transfer-Encoding: chunked
Vary: Accept-Encoding
```

But if I then request the same content with GZip compression, the response uses Brotli compression:

```bash
curl -s -D - https://localhost:5000/test.txt -H "Accept-Encoding: gzip" -o gzip.dat
```

```http
HTTP/1.1 200 OK
Content-Type: text/plain
Date: Tue, 10 Jan 2023 05:33:16 GMT
Server: Kestrel
Accept-Ranges: bytes
Age: 9
Content-Encoding: br
ETag: "1d924af6fef9583"
Last-Modified: Tue, 10 Jan 2023 04:53:10 GMT
Transfer-Encoding: chunked
Vary: Accept-Encoding
```

### Exceptions (if any)

_No response_

### .NET Version

7.0.101

### Anything else?

I can work around this using this code:

```csharp
builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder =>
{
builder.SetVaryByQuery(string.Empty);
builder.VaryByValue(context =>
{
var responseCompressionProvider = context.RequestServices.GetService()!;
var encodingName = responseCompressionProvider.GetCompressionProvider(context)?.EncodingName ?? "none";
return new KeyValuePair("compression", encodingName);
});
builder.Cache();
});
});
```

While this works, it feels like something that should be provided by the framework.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.