How to disable caching of single page application HTML file served through IIS?
Asked Answered
M

7

61

I have a single page application (angular-js) which is served through IIS. How do I prevent caching of HTML files? The solution needs to be achieved by changing content within either index.html or the web.config, as access to IIS through a management console is not possible.

Some options I am currently investigating are:

IIS is version 7.5 with .NET framework 4

Mashe answered 28/10, 2014 at 4:21 Comment(0)
M
68

Adding the following into web.config solution worked across Chrome, IE, Firefox, and Safari:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

  <location path="index.html">
    <system.webServer>
      <httpProtocol>
        <customHeaders>
          <add name="Cache-Control" value="no-cache" />
        </customHeaders>
      </httpProtocol>
    </system.webServer>
  </location>

</configuration>

This will ensure that the that Cache-Control header is set to no-cache when requesting index.html.

Mashe answered 31/10, 2014 at 2:3 Comment(4)
I think this only works when hitting a url that has directly index.html in it.. all of the requests in an SPA have virtual urls and don't map to real location paths. What can be done with that?Postbox
@MichailMichailidis For a SPA you probably have a rule to rewrite your urls to '/'? What if you add that path as well?Handsaw
While this answer is technically accurate, I think it worth pointing out that Cache-Control: no-cache doesn't disable cache but rather forces the client to check the origin server. As such it works concertedly with the etag header or possibly the unreliable last-modifed header, but it does not outright disable cache. From MDN for no-cache: "Caches must check with the origin server for validation before using the cached copy." In other words, the browser may still use a cached copy if a 304 isn't returned by the server.Throat
to address concerns of @MichailMichailidis ensure your catchall rewrite rule is <action type="Rewrite" url="/index.html" /> or see this link for full Web.configCatherinacatherine
V
30

For .NET Core, I used the following.

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

Credit to How to disable browser cache in ASP.NET core rc2?

View answered 26/7, 2017 at 14:6 Comment(5)
I'm not very familiar with React (just trying to fix our cache problem), but for me, when debugging, with a breakpoint on the above if statement, the breakpoint is not hit for index.html. I tried putting the code in both app.UseStaticFiles and app.UseSpaStaticFiles. The second one is hit for favicon.ico and manifest.json, located in the same folder as index.html. Any ideas?Hammered
@Kjell Rilbe, if breakpoint is not being hit for index.html, verify in browser console its being requested from the server and not browser cache. If the browser is caching, then you would want to address scenario of index.html files that previously did not have cache control headers and thus the browser isn't aware of server's change. At the moment I don't recall how to address this(append useless query string to path, clear browser cache once manually might be helpful for debugging). Good luck.View
Thanks, I'll check that once more (think I already did, but to make sure). The breakpoints get hit only twice and that's for the files i mentioned, although the prowser actually seems to fetch a number of other resources. The page index.html is probably fetched as default document/root URL without path or file specified, which is why I put a breakpoint on the if, to see what context.File.name actually comes in in that scenario. But... notthing...Hammered
@KjellRilbe if you are using UseSpa(spa => then you should use spa.DefaultPageStaticFileOptions, note, it won't fire in dev server when using UseAngularCli but if you comment out dev server then build the client app manually, then run it, it will hit the breakpoint. Oh and you don't need the if statement, DefaultPageStaticFileOptions only fires for index.html.Levee
Combining this answer with the answer from @RafaelNeto, I ended up with "no-store, max-age=0", and no need for the "Expires" header (it's ignored when max-age is set). Worked perfectly.Kilogram
O
14

The main premise for SPA is Never cache index.html

Following the MDN recommendations to prevent caching, we must add the Cache-Control HTTP header with the no-store, max-age=0 value to the resource (the index.html file in our case).

Why no-store instead no-cache?

Whith no-store, the resource is not stored anywhere. With no-cache, the resource may be stored, but it should be validated with the server by the store before use it.

Why max-age=0?

Force to clear pre-existing valid cache responses (no-store doesn't).

In IIS we can manage our app cache configuration through the web.config file. Here is a complete web.config file (must be located in the root directory of our application) that includes the cache configuration for the index.html file as well as the routes configuration (I have added the SPA routing and the HTTP redirection to HTTPS as examples):

<configuration>
  <location path="index.html">
    <system.webServer>
      <httpProtocol>
        <customHeaders>
          <add name="Cache-Control" value="no-store, max-age=0" />
        </customHeaders>
      </httpProtocol>
    </system.webServer>
  </location>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="HTTP to HTTPS" enabled="true" stopProcessing="true">
          <match url="(.*)" />
          <conditions>
            <add input="{HTTPS}" pattern="^OFF$" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" />
        </rule>
        <rule name="SPA Routes" enabled="true" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/index.html" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>
Odeen answered 11/8, 2021 at 9:24 Comment(0)
D
10

When serving your html files, you can append a random query string. This will prevent the browser from using the old versions even if the file is in the browser cache.

/index.html?rnd=timestamp

The other option is to add the no-cache setting at IIS level. This adds Cache-Control: no-cache in the response which tells browsers to not cache the file. It works from IIS 7 onwards.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <!-- Note the use of the 'location' tag to specify which 
       folder this applies to-->
  <location path="index.html">
    <system.webServer>
      <staticContent>
        <clientCache cacheControlMode="DisableCache" />
      </staticContent>
    </system.webServer>
  </location>
</configuration>
Denaturalize answered 28/10, 2014 at 4:25 Comment(3)
Regarding adding a timestamp to the URL - yes this would work but I see this as more of a temporary hack rather than a solution. I don't believe any reputable SPA solutions use this method.Mashe
Don't forget your ng routes, css and javascript. The way you do this is web.config for index.html. ?v=1 for everything else e.g. main.css?v=1.Hectograph
Mostly a landing page like index.html is not requested by in-app code but by an end-user whom will just specify the address in the browser and won't append a random timestamp 😂 so "the other option" is the way to go 👍🏼Oracular
C
9
  <meta http-equiv="cache-control" content="no-cache, must-revalidate, post-check=0, pre-check=0">
  <meta http-equiv="expires" content="0">
  <meta http-equiv="pragma" content="no-cache">

Place these meta tags between <head> and </head> in index.html directly. Of course, you can place these tags in any html file you want to ask the browser not to cache. The meta tags are specific to whatever file or files you manually add them to.

Carrefour answered 19/6, 2018 at 9:42 Comment(4)
Does that header apply only to the index.html file or also to everything included by it? (css, js, images,...)Latterly
i think only for index.htmlCarrefour
Really, Sagar.. You think? The word 'index.html' isn't even in that one-liner that you didn't explain.Nucleon
@Nucleon these meta tags are placed statically in the <head> section of the html file. So they apply to that file only. You can place these meta tags in any static html file to request the browser not to cache. Use other methods if you want IIS to turn off browser caching on files more algorithmically.Alysonalysoun
W
9

None of these answers worked for me because, as @MichailMichailidis mentioned, "all of the requests in an SPA have virtual urls and don't map to real location paths", so location path never matches index.html.

I found the solution in this blog post, which links to this answer. Both show you how you can use rewrite module's outboundRules to change the cache-control response header based on a condition.

So this is what my web.config looks like after this configuration:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <outboundRules>
        <rule name="RewriteCacheControlForIndexHtmlFile" 
          preCondition="IsIndexHtmlFile">
          <match serverVariable="RESPONSE_Cache_Control" pattern=".*" />
          <action type="Rewrite" value="max-age=0" />
        </rule>
        <preConditions>
          <preCondition name="IsIndexHtmlFile">
            <add input="{REQUEST_FILENAME}" pattern="index.html" />
          </preCondition>
        </preConditions>
      </outboundRules>
...
Waterman answered 3/12, 2021 at 18:28 Comment(1)
This might actually be better for SPA because you might not want to disable caching for all the files.Indeterminate
B
4

For my case none of previous answears worked, but I found another way, by filter by response content type.

Just add below to web.config

...
<system.webServer>
    <rewrite>
        <outboundRules>
            <rule name="RewriteCacheControlForHtmlFile" preCondition="IsHtmlFile">
                <match serverVariable="RESPONSE_Cache_Control" pattern=".*" />
                <action type="Rewrite" value="no-store, max-age=0" />
            </rule>
            <preConditions>
                <preCondition name="IsHtmlFile">
                    <add input="{RESPONSE_CONTENT_TYPE}" pattern="html$" />
                </preCondition>
            </preConditions>
...
Broadus answered 1/6, 2023 at 10:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.