Skip to content

ALWAYS include the Vary: Accept-Encoding header when response compression is enabled #48008

@aradalvand

Description

@aradalvand

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Currently, the response compression middleware only adds a Vary: Accept-Encoding header to the response, if and only if the request contains an Accept-Encoding header.

This is wrong. The Vary: Accept-Encoding header should be included in the response regardless of whether or not Accept-Encoding was part of the original request. This is because otherwise, if the very first request is one without the Accept-Encoding header (and by extension there is no Vary: Accept-Encoding in the response), an HTTP cache would not know that if a subsequent request came in that did include an Accept-Encoding header, it should NOT use the previous cache entry to satisfy that request.

The value of the Vary header shouldn't be 'conditional' (for a request to the same URL). That's incorrect behavior and would cause issues especially with proxy caches (e.g. CDNs).

This principle is something that was explicitly pointed out in this article by Fastly — which is also mentioned in the MDN page for Vary.

Expected Behavior

The Vary: Accept-Encoding header should be included in every response whose Content-Type MIME type is supported by the response compression middleware, regardless of the presence of Accept-Encoding in the request.

This same principle also applies to any middleware that might add a value to the Vary header of the response, there could be some others in the ASP.NET Core codebase as well, although the response compression middleware is the one I stumbled into.

The relevant piece of code seems to be the following:

private ICompressionProvider? InitializeCompressionHeaders()
{
if (_provider.ShouldCompressResponse(_context))
{
var headers = _context.Response.Headers;
// If the MIME type indicates that the response could be compressed, caches will need to vary by the Accept-Encoding header
var varyValues = headers.GetCommaSeparatedValues(HeaderNames.Vary);
var varyByAcceptEncoding = false;
for (var i = 0; i < varyValues.Length; i++)
{
if (string.Equals(varyValues[i], HeaderNames.AcceptEncoding, StringComparison.OrdinalIgnoreCase))
{
varyByAcceptEncoding = true;
break;
}
}
if (!varyByAcceptEncoding)
{
// Can't use += as StringValues does not override operator+
// and the implict conversions will cause an incorrect string concat https://github.com/dotnet/runtime/issues/52507
headers.Vary = StringValues.Concat(headers.Vary, HeaderNames.AcceptEncoding);
}

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

No response

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-middlewareIncludes: URL rewrite, redirect, response cache/compression, session, and other general middlewaresbugThis issue describes a behavior which is not expected - a bug.feature-response-compressionhelp wantedUp for grabs. We would accept a PR to help resolve this issue

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions