如何禁用通过IIS提供的单页应用程序HTML文件的缓存?

56
我有一个单页应用程序(angular-js),它通过IIS提供服务。如何防止HTML文件被缓存?解决方案需要通过更改index.html或web.config中的内容来实现,因为无法通过管理控制台访问IIS。
目前我正在调查一些选项:
- web.config缓存配置文件 - http://www.iis.net/configreference/system.webserver/caching - web.config客户端缓存 - http://www.iis.net/configreference/system.webserver/staticcontent/clientcache - meta标签 - Using <meta> tags to turn off caching in all browsers? IIS版本为7.5,.NET框架为4。
7个回答

67
将以下内容添加到 web.config 中,可以解决在Chrome、IE、Firefox和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>

这将确保在请求 index.html 时,将 Cache-Control 标头设置为 no-cache

32
我认为这仅适用于直接包含index.html的URL。所有在单页应用程序中的请求都具有虚拟URL,并且不映射到真实位置路径。对此可以做些什么呢? - Michail Michailidis
2
@MichailMichailidis 对于 SPA,您可能有一个将 URL 重写为 '/' 的规则?如果您也添加了该路径会怎么样? - Jason Goemaat
1
虽然这个答案在技术上是准确的,但我认为值得指出的是 Cache-Control: no-cache 并不会禁用缓存,而是强制客户端检查源服务器。因此,它与 etag 头或可能不可靠的 last-modifed 头协同工作,但它并没有完全禁用缓存。来自 MDN 的 no-cache 描述如下:“缓存必须在使用缓存副本之前与源服务器进行验证。”换句话说,如果服务器没有返回 304,则浏览器仍然可以使用缓存副本。 - benbotto
为了解决@MichailMichailidis的问题,请确保您的catchall重写规则是<action type="Rewrite" url="/index.html" />,或者参考此链接获取完整的Web.config - wal

29

对于.NET Core,我使用了以下方法。

        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");
                }
            }
        });

参考如何在ASP.NET Core rc2中禁用浏览器缓存?

的内容。


我对React不是很熟悉(只是试图解决我们的缓存问题),但在调试时,当在上面的if语句上设置断点时,index.html的断点没有被触发。我尝试将代码放在app.UseStaticFilesapp.UseSpaStaticFiles中。第二个选项适用于位于与index.html相同文件夹中的favicon.icomanifest.json。有什么想法吗? - Kjell Rilbe
@Kjell Rilbe,如果无法在“index.html”中设置断点,请在浏览器控制台中验证它是否是从服务器请求而不是浏览器缓存。如果浏览器正在缓存,则需要处理以前没有缓存控制标头的“index.html”文件的情况,因此浏览器不知道服务器的更改。目前我不记得如何解决这个问题(将无用的查询字符串附加到路径中,手动清除浏览器缓存可能有助于调试)。祝你好运。 - ttugates
2
谢谢,我会再检查一遍(我想我已经检查过了,但为了确保)。断点仅被命中两次,而这是我提到的文件,虽然浏览器似乎实际获取了许多其他资源。页面index.html可能作为默认文档/根URL被获取,没有指定路径或文件,这就是我在if上打断点的原因,以查看在这种情况下context.File.name实际上是什么。但是...没有任何反应... - Kjell Rilbe
@KjellRilbe 如果你正在使用 UseSpa(spa =>,那么你应该使用 spa.DefaultPageStaticFileOptions,请注意,在使用 UseAngularCli 时,它不会在开发服务器上触发,但是如果你注释掉开发服务器然后手动构建客户端应用程序,然后运行它,它将会触发断点。哦,而且你不需要 if 语句,DefaultPageStaticFileOptions 只对 index.html 触发。 - Glass Cannon

13

单页应用程序的主要前提是 不要缓存index.html

遵循MDN 推荐的防止缓存的方法,我们必须为资源(在我们的情况下是index.html文件)添加Cache-Control HTTP 头,并将其值设置为 no-store, max-age=0

为什么选用no-store而不是no-cache

使用no-store,资源不会被存储在任何地方。而使用no-cache,资源可能会被存储,但在使用之前需要由存储器与服务器进行验证。

为什么选择max-age=0

强制清除预先存在的有效缓存响应(no-store无法做到这一点)。

在IIS中,我们可以通过web.config文件来管理应用程序的缓存配置。以下是一个完整的web.config文件(必须位于我们应用程序的根目录中),其中包括index.html文件的缓存配置以及路由配置(我添加了SPA路由和HTTP重定向到HTTPS作为示例):

<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>

10
在提供HTML文件时,您可以附加一个随机查询字符串。这样做可以防止浏览器使用旧版本,即使该文件在浏览器缓存中存在也是如此。
/index.html?rnd=timestamp

另一种选择是在IIS级别添加no-cache设置。这将在响应中添加Cache-Control: no-cache,告诉浏览器不要缓存文件。它适用于IIS 7及以上版本。
<?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>

1
关于在URL中添加时间戳 - 是的,这样做可以起作用,但我认为这更像是一个临时的hack而不是一个解决方案。我不相信任何值得信赖的SPA解决方案会使用这种方法。 - Andrew
不要忘记你的 ng 路由、CSS 和 JavaScript。你可以在 web.config 中为 index.html 设置,对于其他所有东西,如 main.css?v=1。 - DalSoft
通常像index.html这样的着陆页不是由应用程序代码请求的,而是由最终用户在浏览器中指定地址并且不会附加随机时间戳,因此“另一种选择”是正确的方式。 - Youp Bernoulli

9
  <meta http-equiv="cache-control" content="no-cache, must-revalidate, post-check=0, pre-check=0">


4
这个标题只适用于index.html文件还是包括它引用的所有内容(如css、js和图片等)? - johey
我认为只针对index.html。 - Sagar Jadhav
真的吗,Sagar.. 你"认为"吗?那个你没有解释的一行代码里甚至没有出现'index.html'这个词。 - undefined
@Zimano 这些元标签是静态放置在HTML文件的<head>部分。因此,它们仅适用于该文件。您可以将这些元标签放置在任何静态HTML文件中,以请求浏览器不要缓存。如果您希望IIS更加算法化地关闭浏览器对文件的缓存,请使用其他方法。 - undefined

7

对我来说,这些答案都不起作用,因为正如@MichailMichailidis所提到的那样,"在SPA中的所有请求都有虚拟URL,并且不映射到真实位置路径",所以位置路径永远不会匹配index.html

我在这篇博客文章中找到了解决方案,其中链接到此答案。两者都展示了如何使用重写模块的outboundRules根据条件更改cache-control响应头。

因此,在进行这项配置后,我的web.config看起来像这样:

<?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>
...

2

对于我的情况,之前的答案都没有起作用,但我找到了另一种方法,通过响应内容类型进行过滤。

只需将以下内容添加到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>
...

1
这个解决方案对我有效,谢谢。 - waseemrakab

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接