12

I am creating a website using .Net core and angular 5. I created the project using the latest .Net core angular template (using dotnet new angular with .Net core 2.1 installed).

This project uses the angular cli to build/package and applies a hash onto the bundled js and css files:

enter image description here

However after publishing my site to an azure app service I found that when I browsed to the site I would see the old version until I manually refreshed with F5 (Ctrl-F5 wasn't necessary). This seems to be because although the js/css files wont be cached, the index.html page which contains the references to these files will be served from the cache.

When I press F5 to reload the website the index.html (home below) is requested from the site (304 in this case as it hasn't changed, if it has it will get the latest):

enter image description here

However when I initially load the page (via a bookmark or typing in the address etc) the page is directly served from cache:

enter image description here

Is this expected behaviour? Why is it different loading the page for the first time vs pressing F5? And can I/should I prevent this caching?

QTom
  • 1,441
  • 1
  • 13
  • 29

2 Answers2

19

Here's what I ended up with after combining a bunch of answers. My goal was to never cache index.html. While I was in there, and since Angular nicely cache-busts the js and css files, I had it cache all other assets for a year.

Just make sure you're using a cache-busting mechanism for assets, like images, that you're managing outside of Angular.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  // ...
  app.UseStaticFiles();

  if (env.IsDevelopment())
  {
    // no caching
    app.UseSpaStaticFiles();
  }
  else
  {
    app.UseSpaStaticFiles(new StaticFileOptions
    {
      OnPrepareResponse = context =>
      {
        context.Context.Response.Headers.Add("Cache-Control", "max-age=31536000");
        context.Context.Response.Headers.Add("Expires", "31536000");
      }
    });
  }

  // ...

  app.UseSpa(spa =>
  {
    spa.Options.DefaultPageStaticFileOptions = new StaticFileOptions
    {
      OnPrepareResponse = context =>
      {
        // never cache index.html
        if (context.File.Name == "index.html")
        {
          context.Context.Response.Headers.Add("Cache-Control", "no-cache, no-store");
          context.Context.Response.Headers.Add("Expires", "-1");
        }
      }
    };
  });
}

Other StackOverflow Answers: Disable Caching in .Net Core | Cache for a year

JackMorrissey
  • 2,567
  • 2
  • 21
  • 18
  • The source you are referring to uses a modification of `app.UseStaticFiles()`, you are using a modification of `app.UseSpaStaticFiles` and indicate that without any parameters no caching is being done. Later within `UseSpa(spa =>` you continue with another approach to disable caching for html files. A bit confusing as to what would work and what not. When debugging both solutions, index.html does not come up and therefor does not add any headers to this file. I see that within `UseSpaStaticFiles`, all image, svg and json files are passing by and I can correctly control these files.. – CularBytes Aug 20 '19 at 16:05
  • ..but the breakpoint within `DefaultPageStaticFileOptions` never gets hit. Atleast within the development environment. – CularBytes Aug 20 '19 at 16:10
  • After publishing it to a test environment, I notice that there is a difference between dev and 'production'. I did not use the `if-else` for `env.IsDevelopment()` and I did in both `OnPrepareResponse` an equal `if-else` condition for index.html. One of the options configured correctly to apply `no cache, no-store`. – CularBytes Aug 21 '19 at 07:13
  • 1
    Thanks for this. A minor correction - the only other directive that applies when using `no-store` is `max-age`; `no-cache` is a conflicting directive that has no effect. MDN recommends using `no-store, max-age=0` if you want to clear any existing cached responses, or just `no-store` if you only want to prevent caching going forward. Source: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#preventing_caching – Paul Sep 22 '21 at 16:51
2

Not a neat or perfect solution but this seems to have worked and might get people on the right track:

In Configure() in Startup.cs I added this

app.Use(async (c, next) =>
{
    if (c.Request.Path == "/")
    {
        c.Response.Headers.Add("Cache-Control", "no-store,no-cache");
        c.Response.Headers.Add("Pragma", "no-cache");
    }
    await next();
});

Since adding this I haven't been able to reproduce my issue.

QTom
  • 1,441
  • 1
  • 13
  • 29
  • Will this cache every single file or just index.html file ? I didn't really get who the c.Request.Path == "/" check match the index.html file – Zhenyi Zhang Jul 06 '20 at 16:14
  • @ZhenyiZhang This will disable caching by adding the no-cache headers. The request path check ensures this is only applied when the request path is empty (i.e it will apply when the request is www.example.com, but not www.example.com/api/foo). – QTom Jul 07 '20 at 10:29
  • @QTom any request can return the index.html if its the client's first time navigating to that page. IE: if I open my browser and go to www.example.com/api/foo immediately, then it will return the index.html but will not be cached because request path was not empty. – Hedgybeats Jul 01 '22 at 12:48