Search code examples
c#.netetag

ASP.NET Core 8 TypedResults.File with ETag always returning result from browser disk cache


I have a simple ASP.NET Core 8 endpoint that returns a TypedResults.File with an ETag. First time I hit the endpoint, the file is returned 100% OK.

Second (and subsequent) times I hit the endpoint with the same instance of the browser I get an HTTP 200 OK (from disk cache) - it never makes the actual request to my localhost server.

Here's the network traffic of the 2x requests:

Request 1:

enter image description here

Request 2 (which grabs it from the disk cache)

enter image description here

I thought that if the initial request returns an ETag then the browser is "smart enough" to do subsequent requests with the ETag + "If-None-Match" request header?

This is the code that generated the File

var etag = new EntityTagHeaderValue($"\"{invoicePdfVersion}-{lastModifiedOn.Ticks}\"");

return TypedResults.File(
    pdfData, // byte array of pdf data 
    "application/pdf", // content type
    "testing", // filename
    false, // enable range processing
    lastModifiedOn, // DateTime when the file was last updated/modified
    etag); // etag value

I understand that I could add this to the response:

context.Response.Headers.CacheControl = "no-cache";

which I think does stop the disk cache occurring but I'm not sure if this is required? Shouldn't the MS-Code for ReturnTypes.File set all the headers up correctly so I don't need to do anything else?

I have some server side code which checks the ETag:

if (context.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var ifNoneMatch) &&
    ifNoneMatch == etag.ToString())
{
    return TypedResults.StatusCode(304); // HTTP NotModified.
}

but of course, this never gets hit.

What can I try next?


Solution

  • Relying on ETag to always check with the server requires that the response headers include the Cache-Control header with the value no-cache.

    Quoting from MDN:

    The no-cache response directive indicates that the response can be stored in caches, but the response must be validated with the origin server before each reuse, even when the cache is disconnected from the origin server.

    The boldface part justifies this answer.