【问题标题】:When do browsers refresh a stale cache entry?浏览器何时刷新过时的缓存条目?
【发布时间】:2014-02-23 11:22:23
【问题描述】:

我一直在努力刷新对 HTTP/1.1 缓存的理解——我发现我必须每隔一段时间就做一次,因为我的大脑似乎有太多可能的组合无法可靠地记住。

我认为Expiresmax-age 缓存控制指令被浏览器用作提示,而不是铁律:如果缓存条目过时(超过其最大年龄),浏览器可以验证它。

我和一位同事为此发生了争执,他强迫我阅读 RFC,我觉得这有点苛刻,但证明他完全正确:如果我理解正确,则不允许客户使用缓存他们知道是陈旧的条目。

换句话说:如果一个文档指定了 max-age 缓存头,并且没有其他指令(例如 must-validate)影响缓存行为,并且该文档的缓存条目变得陈旧,浏览器将始终重新验证它。

这是完全清楚和合乎逻辑的,为了搁置这个论点,我开始通过让服务器提供具有以下标头的测试 JavaScript 文件来凭经验确认它:

< HTTP/1.1 200 OK
< Content-Type: application/x-javascript; charset=UTF-8
< Cache-Control: public, max-age=30
< Date: Thu, 30 Jan 2014 22:11:28 GMT
< Accept-Ranges: bytes
< Server: testServer/1.0
< Vary: Accept-Encoding
< Transfer-Encoding: chunked

此 JavaScript 文件通过文档的 &lt;head&gt; 中的 &lt;script&gt; 元素包含在同一服务器上托管的 HTML 页面中。 HTML 页面提供以下缓存控制标头:

Cache-Control: no-cache, must-revalidate, no-store, max-age=0

加载 HTML 页面后,我点击进入另一个页面,等待 5 分钟,然后点击返回原始页面的链接,监控所有 HTTP 请求 - 从未请求过测试文件。这在 Firefox 和 Safari 的最新版本中得到了一致的复制。

这有点冗长,但我的问题的要点是:我的测试似乎表明主流浏览器不尊重 RFC,并且不会重新验证过时的缓存条目。我是否误解了 RFC?同样可能的是,我是否搞砸了我的测试,有人能证明他们错了吗?还是浏览器真的不尊重max-age 缓存指令?

【问题讨论】:

  • HTTP/1.0HTTP/1.1?顺便说一句,我和你在一起,我不认为浏览器在缓存方面会遵守 RFC 中的必须...
  • 好点,谢谢。我已编辑问题以指定HTTP/1.1
  • 如何让浏览器请求该文件,是否将其用作 html 文档中脚本标签的来源?该文档使用什么缓存标头提供服务? “导航离开并返回”是什么意思,(如何)刷新包含页面?
  • 我已经更新了问题:脚本是通过&lt;script&gt; 标签包含的,我不刷新包含页面而是从外部链接加载它,“导航离开”意味着进入另一个页面(为了不用强行刷新就可以回到原来的那个),HTML页面提供Cache-Control: no-cache, must-revalidate, no-store, max-age=0
  • 这意味着当你点击返回时,包含页面将被重新请求。你能看到这种情况吗(你指的是 html 或 js 文件的“测试文件”)吗?页面上实际使用了 JavaScript 吗?

标签: http caching


【解决方案1】:

在@CodeCaster 的大量挖掘和帮助之后,这个问题的规范答案是浏览器似乎确实尊重 RFC:陈旧的缓存条目总是被重新验证,除非在非常特殊的情况下它们被直接访问或间接地,通过浏览器的历史。在这种情况下,RFC 的13.3 部分适用:

历史机制和缓存是不同的。特别是历史机制不应该试图显示资源当前状态的语义透明视图。相反,历史机制旨在准确显示用户在检索资源时所看到的内容。

【讨论】:

    【解决方案2】:

    我无法重现您的问题。我尝试使用 ASP.NET MVC,使用以下代码:

    public ActionResult Index()
    {
        Response.AddHeader("Cache-Control", 
                           "no-cache, must-revalidate, no-store, max-age=0");
        return View();
    }
    
    public ActionResult JavaScript()
    {
        Response.AddHeader("Cache-Control", "public, max-age=30");
        return View();
    }
    
    public ActionResult Page2()
    {
        return View();
    }
    

    第一个操作返回索引页面(仅相关标题):

    HTTP/1.1 200 OK
    Cache-Control: no-cache, no-store, must-revalidate, max-age=0
    Pragma: no-cache
    Expires: -1
    Vary: Accept-Encoding
    Content-Length: 182
    
    
    <html>
        <head>
            <script src="/Home/JavaScript" type="text/javascript"></script>
        </head>
        <body>
            <a href="/Home/Page2">Page 2</a>
        </body>
    </html>
    

    JavaScript 是这样返回的:

    HTTP/1.1 200 OK
    Cache-Control: public, max-age=30
    Vary: Accept-Encoding
    Content-Length: 24
    
    
    document.write('Foo');
    

    Page2 仅包含一个返回主页的链接。

    现在,当我使用 Internet Explorer 11、Chrome 32 或 Firefox 26 时,我看到以下行为:

    • 第一次向/ 发出请求时,会请求索引文档以及JavaScript 文件,并如上图所示返回,并使用Fiddler 进行验证。
    • 当我单击“第 2 页”链接时,仅请求第 2 页,因为它只包含返回第 1 页的链接。
    • 现在,当我在 30 秒内从第 2 页单击“主页”链接时,JS 文件在任何浏览器中不会再次请求。
    • 当我在第 2 页上等待一段时间(> 30 秒)然后单击“主页”链接时,所有三个浏览器都请求 JS 文件。

    但是,当我在任何时间(大于或小于 30 秒)后在任何浏览器中单击第 2 页的返回按钮时,始终是索引文件,但 JS 文件是从不在任何浏览器中再次请求。

    它需要刷新 (F5) 或通过单击“第 2 页”然后再次单击“主页”来导航,以使浏览器在 JS 文件过时后对其执行新的请求。

    【讨论】:

    • 您的第一个发现(链接到/ 时 30 秒后重新加载 JS)与我自己的不匹配,但我更倾向于相信您的结果而不是我的结果:它们符合RFC 所说的应该发生的事情。另一方面,回溯历史时它没有被重新加载的事实对我来说似乎明显违反了 RFC。你似乎不同意,我对你的理由很感兴趣。
    • @Nicolas 我只是报告我所看到的。 :-) 奇怪的是,您的浏览器似乎对相同的标头显示不同的行为。我也希望通过单击来启动 JS 文件的重新加载,因为浏览器知道它已经过时了,而且它似乎确实对索引文档这样做了。我猜需要更多的试验(或阅读浏览器的问题跟踪器、邮件列表或源代码)来找出发生这种情况的原因。在有更多知识渊博的人出现之前,请随意不要接受这个答案。
    • 在对我的测试进行三次检查后,您的结果是正确的:通过链接返回根页面导致过时条目被重新验证,通过浏览器的历史不会。经过一番挖掘,我发现 RFC 的 13.3 部分解释了为什么这是预期的行为。
    猜你喜欢
    • 2013-03-06
    • 2018-02-16
    • 2011-05-05
    • 1970-01-01
    • 2013-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-17
    相关资源
    最近更新 更多