Hi,
I seem to be facing a problem when deploying a new build to my server.
It seems that index.html is cached by the browser and tries to load the old bundle, which leads to a 404.
Any ideas how to fix this?
Thanks!
Hi. It looks like this is a question about how to use ASP.NET Core. While we do our best to look through all the issues filed here, to get a faster response we suggest posting your questions to StackOverflow using the asp.net-core-mvc tag.
@georgiosd, you can configure the Cache-Control headers for static files as documented here: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/static-files?view=aspnetcore-2.0&tabs=aspnetcore2x
@mkArtakMSFT the SPA templates use UseSpaStaticFiles which also has an overload to set options but from what I could see it doesn't have an overload the allows setting BOTH the SourcePath and the cache options.
You're right:, @georgiosd. https://github.com/aspnet/JavaScriptServices/blob/15d2f5a898da584433e38c82d5b09c375d9f87b7/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/SpaStaticFilesExtensions.cs#L114
@SteveSandersonMS, any suggestions here?
@mkArtakMSFT I could have been more specific :)
As long as the StaticFileOptions you've passed don't specify a FileProvider, then it will automatically pick up the RootPath from your DI config: https://github.com/aspnet/JavaScriptServices/blob/15d2f5a898da584433e38c82d5b09c375d9f87b7/src/Microsoft.AspNetCore.SpaServices.Extensions/StaticFiles/SpaStaticFilesExtensions.cs#L95
If you choose to pass a custom FileProvider (which you don't need to if your only goal is to change the caching response headers) then it's up to you to configure it to load files from the desired source location.
I'm not totally sure what you mean by SourcePath - I'm guessing you meant RootPath, but apologies if I am misunderstanding.
Actually I think the confusion is on my end.
I was referring to:
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
But that seems to be a different call. The relevant one is:
app.UseStaticFiles();
app.UseSpaStaticFiles(/* static file options */);
@SteveSandersonMS is the call to UseStaticFiles necessary or does UseSpaStaticFiles suffice?
Looks like this issue is troubling more people:
https://github.com/facebook/create-react-app/issues/1910
https://stackoverflow.com/questions/49604821/cache-busting-with-create-react-app
Any many more...
This seems to be because of the service worker from CRA so the cache settings I added don't seem to have much of an effect.
If you have any ideas let me know :)
This setup works for me (Create React App hosted on Azure App Service).
``c#
app.UseSpaStaticFiles(new StaticFileOptions()
{
OnPrepareResponse = ctx =>
{
if (ctx.Context.Request.Path.StartsWithSegments("/static"))
{
// Cache all static resources for 1 year (versioned filenames)
var headers = ctx.Context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue
{
Public = true,
MaxAge = TimeSpan.FromDays(365)
};
}
else
{
// Do not cache explicit/index.htmlor any other files. See also:DefaultPageStaticFileOptions` below for implicit "/index.html"
var headers = ctx.Context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue
{
Public = true,
MaxAge = TimeSpan.FromDays(0)
};
}
}
});
```c#
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
spa.Options.DefaultPageStaticFileOptions = new StaticFileOptions()
{
OnPrepareResponse = ctx => {
// Do not cache implicit `/index.html`. See also: `UseSpaStaticFiles` above
var headers = ctx.Context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue
{
Public = true,
MaxAge = TimeSpan.FromDays(0)
};
}
};
if (env.IsDevelopment())
{
//spa.UseReactDevelopmentServer(npmScript: "start");
spa.UseProxyToSpaDevelopmentServer("http://localhost:3000");
}
});
Cache-Control: public, max-age=0/static/* resources for 1 year (versioned/sha'd filenames like javascript bundles)Cache-Control: public, max-age=31536000manifest.json, service-worker.js, etc)Cache-Control: public, max-age=0The main issue I had before making this change was because there was not an explicit Cache-Control header but there was a Last-Modified header, browsers would use this to cache the response.
Finally, if neither header is present, then we look for a "Last-Modified" header. If this header is present, then the cache's freshness lifetime is equal to the value of the "Date" header minus the value of the "Last-modified" header divided by 10. This is the simplified heuristic algorithm suggested in RFC 2616 section 13.2.4.
I can confirm that @techniq 's solution also works for me. Thanks!
Thanks for contacting us. We believe that the question you've raised have been answered. If you still feel a need to continue the discussion, feel free to reopen it and add your comments.
I tried the solution from @techniq but it is now working, and breakpoints in OnPrepareResponse are never hit.
I copied the code exactly, I also tried without the checks so everything would be given a cache of 365 days, but everything is returned with cache-control | public, max-age=0 so for me, at least, the question has not been answered.
@mkArtakMSFT what is your solution?
Most helpful comment
This setup works for me (Create React App hosted on Azure App Service).
``
c# app.UseSpaStaticFiles(new StaticFileOptions() { OnPrepareResponse = ctx => { if (ctx.Context.Request.Path.StartsWithSegments("/static")) { // Cache all static resources for 1 year (versioned filenames) var headers = ctx.Context.Response.GetTypedHeaders(); headers.CacheControl = new CacheControlHeaderValue { Public = true, MaxAge = TimeSpan.FromDays(365) }; } else { // Do not cache explicit/index.htmlor any other files. See also:DefaultPageStaticFileOptions` below for implicit "/index.html"var headers = ctx.Context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue
{
Public = true,
MaxAge = TimeSpan.FromDays(0)
};
}
}
});
Cache-Control: public, max-age=0/static/*resources for 1 year (versioned/sha'd filenames like javascript bundles)Cache-Control: public, max-age=31536000manifest.json,service-worker.js, etc)Cache-Control: public, max-age=0The main issue I had before making this change was because there was not an explicit
Cache-Controlheader but there was aLast-Modifiedheader, browsers would use this to cache the response.