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

Posted

技术标签:

【中文标题】如何禁用通过 IIS 提供的单页应用程序 HTML 文件的缓存?【英文标题】:How to disable caching of single page application HTML file served through IIS? 【发布时间】:2014-12-23 09:58:37 【问题描述】:

我有一个通过 IIS 提供服务的单页应用程序 (angular-js)。如何防止缓存 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 元标签 - Using <meta> tags to turn off caching in all browsers?

IIS 是 .NET 框架 4 的 7.5 版

【问题讨论】:

【参考方案1】:

在提供您的 html 文件时,您可以附加一个随机查询字符串。即使文件在浏览器缓存中,这也会阻止浏览器使用旧版本。

/index.html?rnd=timestamp

另一个选项是在 IIS 级别添加无缓存设置。这会在响应中添加 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>

【讨论】:

关于在 URL 中添加时间戳 - 是的,这可行,但我认为这更像是一种临时破解,而不是解决方案。我不相信任何有信誉的 SPA 解决方案都使用这种方法。 不要忘记您的 ng 路由、css 和 javascript。您执行此操作的方式是 web.config for index.html。 ?v=1 对于其他所有内容,例如main.css?v=1. 大多数情况下,像 index.html 这样的登陆页面不是由应用内代码请求的,而是由最终用户请求的,他们只需在浏览器中指定地址,不会附加随机时间戳?所以“另一种选择”是要走的路??【参考方案2】:

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

【讨论】:

我认为这仅在点击直接包含 index.html 的 url 时才有效。SPA 中的所有请求都有虚拟 url,并且不映射到真实的位置路径。可以用它做什么? @MichailMichailidis 对于 SPA,您可能有一个规则将您的网址重写为“/”?如果您也添加该路径怎么办? 虽然这个答案在技术上是准确的,但我认为值得指出的是 Cache-Control: no-cache 不会禁用缓存,而是强制客户端检查源服务器。因此,它与etag 标头或可能不可靠的last-modifed 标头协同工作,但它不会彻底禁用缓存。来自 no-cache 的 MDN:“在使用缓存副本之前,缓存必须与源服务器检查以进行验证。”换句话说,如果服务器没有返回 304,浏览器可能仍然使用缓存副本。 为了解决@MichailMichailidis 的问题,请确保您的全面重写规则为&lt;action type="Rewrite" url="/index.html" /&gt; 或查看this link 以获取完整的Web.config【参考方案3】:

对于 .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");
                
            
        );

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

【讨论】:

我对 React 不是很熟悉(只是想解决我们的缓存问题),但是对我来说,在调试的时候,在上面的if 语句上有一个断点,这个断点没有命中index.html。我尝试将代码放在app.UseStaticFilesapp.UseSpaStaticFiles 中。第二个是favicon.icomanifest.json,位于与index.html 相同的文件夹中。有什么想法吗? @Kjell Rilbe,如果没有为index.html 命中断点,请在浏览器控制台中验证它是从服务器请求的,而不是浏览器缓存。如果浏览器正在缓存,那么您需要解决以前没有缓存控制标头的index.html 文件的情况,因此浏览器不知道服务器的更改。目前我不记得如何解决这个问题(将无用的查询字符串附加到路径,手动清除浏览器缓存一次可能有助于调试)。祝你好运。 谢谢,我会再检查一次(我想我已经做过了,但要确保)。断点只被命中两次,这就是我提到的文件,尽管浏览器实际上似乎获取了许多其他资源。页面index.html 可能是作为默认文档/根 URL 获取的,没有指定路径或文件,这就是为什么我在if 上放置一个断点,以查看context.File.name 在该场景中实际出现的内容。但是……没什么……【参考方案4】:
  <meta http-equiv="cache-control" content="no-cache, must-revalidate, post-check=0, pre-check=0">

【讨论】:

该标头仅适用于 index.html 文件还是也适用于其中包含的所有内容? (css, js, 图片,...) 我认为只适用于 index.html【参考方案5】:

SPA 的主要前提是从不缓存 index.html

在MDN recommendations to prevent caching 之后,我们必须添加 Cache-Control HTTP 标头和 no-store, max-age=0 资源的值(在我们的例子中是 index.html 文件)。

为什么 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_HOSTREQUEST_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>

【讨论】:

【参考方案6】:

这些答案都不适合我,因为正如@MichailMichailidis 所提到的,“SPA 中的所有请求都有虚拟 url,并且不映射到真实的位置路径”,所以 location path从不匹配index.html

我在这个blog post 中找到了解决方案,它链接到this answer。两者都向您展示了如何使用重写模块的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>
...

【讨论】:

以上是关于如何禁用通过 IIS 提供的单页应用程序 HTML 文件的缓存?的主要内容,如果未能解决你的问题,请参考以下文章

HTML6 无 JavaScript 的单页应用引起一片哗然

IIS / AngularJS单页应用程序-在应用程序中导航时如何防止单个index.html的页面缓存

如何设置NGINX以根据位置(在相同的server_name下)部署不同的单页应用程序(SPA的...即静态文件)和子路由

带有 HTML5 应用缓存的单页应用的 Nginx 配置

在具有不同主页的单页应用程序中路由

如何禁用 vuejs 路由器链接?