18

I can't seem to enable caching of static files in ASP.NET Core 2.2. I have the following in my Configure:

public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
  if (env.IsDevelopment()) {
    app.UseDeveloperExceptionPage();
    app.UseCors(...);
  }
  else {
    app.UseHsts();
  }

  app.UseHttpsRedirection();
  app.UseAuthentication();
  app.UseSignalR(routes => { routes.MapHub<NotifyHub>("/..."); });

  app.UseResponseCompression();
  app.UseStaticFiles();
  app.UseSpaStaticFiles(new StaticFileOptions() {
    OnPrepareResponse = (ctx) => {
      ctx.Context.Response.Headers[HeaderNames.CacheControl] = "public, max-age=31557600"; // cache for 1 year
    }
  });
  app.UseMvc();

  app.UseSpa(spa => {
    spa.Options.SourcePath = "ClientApp";
    if (env.IsDevelopment()) {
      spa.UseVueCli(npmScript: "serve", port: 8080);
    }
  });
}

When I try and Audit the production site on HTTPS using chrome I keep getting "Serve static assets with an efficient cache policy":

audit screenshot

In the network tab there is no mention of caching in the headers, when I press F5 it seems everything is served from disk cache. But, how can I be sure my caching setting is working if the audit is showing its not?

network tab screenshot

Colton Scottie
  • 807
  • 1
  • 8
  • 22

2 Answers2

16

This is working in ASP.NET Core 2.2 to 3.1:

I know this is a bit similar to Fredrik's answer but you don't have to type literal strings in order to get the cache control header

app.UseStaticFiles(new StaticFileOptions()
{
    HttpsCompression = Microsoft.AspNetCore.Http.Features.HttpsCompressionMode.Compress,               
    OnPrepareResponse = (context) =>
    {
        var headers = context.Context.Response.GetTypedHeaders();
        headers.CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue
        {
            Public = true,
            MaxAge = TimeSpan.FromDays(30)
        };
    }
});
JHBonarius
  • 10,824
  • 3
  • 22
  • 41
HMZ
  • 2,949
  • 1
  • 19
  • 30
  • you are missing Expires header. – Ali Jul 08 '20 at 07:29
  • 2
    @Ali If a response contains both the `Expires` header and the `max-age` directive, `max-age` takes precedence. Reference: 13.2.4 Expiration Calculations - https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html The max-age directive takes priority over Expires, so if max-age is present in a response, the calculation is simply: – webStuff Nov 06 '20 at 01:18
  • 4
    Note: it is not required to set the `HttpsCompressionMode`! – JHBonarius Jan 12 '21 at 18:43
11

I do not know what UseSpaStaticFiles is but you can add cache options in UseStaticFiles. You have missed to set an Expires header.

// Use static files
app.UseStaticFiles(new StaticFileOptions {
    OnPrepareResponse = ctx =>
    {
        // Cache static files for 30 days
        ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=2592000");
        ctx.Context.Response.Headers.Append("Expires", DateTime.UtcNow.AddDays(30).ToString("R", CultureInfo.InvariantCulture));
    }
});

Beware that you also need a way to invalidate cache when you make changes to static files.

I have written a blog post about this: Minify and cache static files in ASP.NET Core

Andrii Viazovskyi
  • 777
  • 2
  • 9
  • 15