如何防止返回304状态码的请求

68
什么情况下浏览器不会向服务器请求文件?
换句话说,我有一个正在提供的JavaScript文件。它的HTTP响应头具有ETag、Cache-Control: public和Expires: Tue, 19 Jan 2038 03:14:07 GMT。
浏览器缓存被预先涂抹后,服务器返回304。
我的问题是,为什么浏览器第一次访问时甚至需要检查服务器并获得304?我不希望浏览器去询问是否有新版本——它应该直接从浏览器缓存中加载,而不需要与提供脚本的服务器检查修改。
哪些HTTP响应头的组合可以实现这一点?

4
谁设置了过期标头?根据 这个页面,过期标头不应超过一年。另一方面,根据这个页面所述,最大支持日期是2038年1月17日19:14:07 GMT,因为这是32位Unix时间/日期格式支持的最大值。你的日期比这晚了几天,也许这就是原因... - user1429080
2
@user1429080,你链接的RFC已经过时了(请注意https://tools.ietf.org/html/rfc2616上方的顶部横幅;在假定它们代表当前规范之前,您应始终检查tools.ietf.org上的过时RFC)。但是,你的观点仍然相关。虽然当前相关的规范[RFC 7234](https://tools.ietf.org/html/rfc7234)已经删除了建议将到期日期设置在未来1年以上的建议,但它确实警告说应该避免在非常遥远的未来设置日期以防止溢出。 - Mark Amery
@MarkAmery 感谢您指出 tools.ietf.org,我会把它加入书签。关于过期头信息:如果未来日期实际上是问题的原因,我会感到惊讶,但既然我发现了它,我认为应该提一下... - user1429080
我有相反的问题。我希望浏览器能够检查新版本。许多问题都是由于浏览器缓存引起的,当它没有检查到时,显然是由于某些神秘的算法。虽然我没有考虑到‘Expires'头部可能也会发挥作用(和‘Prama'一起)。即使它起作用了,当有一个新版本时会发生什么?如果浏览器从不询问,它将如何找到?您想雇用数百万呼叫中心员工来重复告诉用户清除他们的浏览器缓存吗? - Jake
5个回答

102
首先,相关的HTTP规范在RFC 7234中。如果您查看规范,您会发现两件事:
  • 规范从未在任何情况下要求缓存不重新验证即提供已缓存内容。规范有很多地方指出缓存不能使用已缓存的内容来满足请求,但没有一个地方规定它必须这样做或者必须不重新验证。因此,浏览器厂商始终可以选择重新验证。
  • 其次,您并没有做错什么。根据您返回的标头,浏览器可以缓存响应并使用这些缓存响应。关键点在第4节中,其中指出了提供缓存响应的一个条件是:响应是:
    • 新鲜的(参见第4.2节),或

    • 允许过期的(参见第4.2.4节),或

    • 成功验证的(参见第4.3节)。

    由于您输出的Expires标头在遥远的将来,且该时间尚未到达,因此响应是“新鲜的”,因此不需要重新验证。因此,您在自己的端上做了规范建议的一切。(虽然使用“Cache-Control:max-age=foo”比使用“Expires:”标头设置缓存到期时间更现代化)。

因此,如果您想更改浏览器的缓存行为,则没有希望可言。

然而,情况可能没有你想的那么糟糕。你可能只是在测试时在浏览器中刷新页面,因此只看到了一个请求和304响应。浏览器根据触发它们的请求方式来处理缓存资源。

我进行了一个简单的测试,创建了一个包含指向JS文件的<script>标签、指向图片的<img>标签和指向CSS样式表的<link>标签的HTML页面。这些文件都托管在配置为使用以下设置提供服务的Apache服务器上:

  • E-Tag头文件,
  • 最后修改日期,
  • Cache-Control: max-age=172800头文件

显然,在首次加载页面时,所有资源都以200代码提供服务。此后,在Chrome或Firefox安装默认设置的测试中,我发现:

  • 如果您通过F5键或刷新按钮刷新页面,则页面和所有资源将重新验证(即为每个资源发送一个请求并返回304)。
  • 如果您通过链接或在新选项卡中输入URL返回页面,则不会进行重新验证(即不会发出任何请求)。
  • 在Chrome中,如果您选择URL栏并按Enter键刷新页面,则页面本身会重新验证,但不会有其他资源。在Firefox中,页面和资源都不会重新验证。

这个页面表明Internet Explorer有相同的行为:

有许多情况下,Internet Explorer需要检查缓存条目是否有效:

  • 缓存条目没有到期日期,并且在浏览器会话中首次访问内容
  • 缓存条目具有到期日期,但已过期
  • 用户通过单击“刷新”按钮或按F5键请求页面更新。

换句话说,通常情况下,只有在用户明确刷新页面时才会看到这些重新验证请求。除非您对浏览器缓存的行为有特别要求,否则此行为似乎完全合理。

GoogleMozilla都有一些关于HTTP缓存的文档(我找不到任何等效的MSDN或苹果开发者网站),但没有建议存在任何供应商特定的缓存标头可用于修改浏览器选择何时重新验证的规则。你想做的根本不可能。

如果您真正需要更多控制此行为的方法,可以研究HTML5应用程序缓存或使用HTML5本地存储自己的缓存逻辑,例如 basket.js所做的那样。


我猜测,HTML5 应用缓存是 Chris 寻找的内容 - 很棒的答案。 - Quicker
2
这是一个非常好的答案。现在,您还可以提到ServiceWorker作为一种选择,以更好地控制缓存资源。 - fabiomcosta
1
虽然这一切都很有道理,但有人知道为什么我看到的东西不同吗?我有一个JavaScript文件,起初需要正常请求下载文件(200)。随后的请求将会给出304(刷新页面)或本地缓存版本(跟随链接或在URL栏上按Enter)。但是在随机时间,按Enter键会进行304重新验证。所以Chrome会在304和缓存之间随机切换。 - georaldc

0
如果您和我一样,已经将Angular应用程序部署到IIS上,请确保您的web.config已正确配置以重写URL,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <httpErrors errorMode="Detailed" />
        <rewrite>
            <rules>
                <rule name="Angular Routes" 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="/NameOfYourApp_UnderDefaultWebSite/" />
                </rule>
            </rules>
        </rewrite>
  </system.webServer>
</configuration>

请特别注意在 <action type="Rewrite" url="/NameOfYourApp_UnderDefaultWebSite/" /> 中 url 的值。

1
重写如何帮助缓存? - Bruno Santos
不确定缓存的影响。也许需要另一个问题,并参考这篇文章? - Adam Cox
我的意思是,如果这是一个缓存问题,你的解决方案如何解决304问题? - Bruno Santos

0

Expires:Cache-Control: max-age=应该可以解决问题。您是否在服务器日志中确认浏览器实际上正在进行网络调用?我发现例如Firebug的输出很令人困惑,它会让你误以为你正在进行远程调用,而实际上你只是在访问缓存。


0
在我的一个特定文件的缓存策略中添加了"must-revalidate"之后,我遇到了这样的情况。之前这个文件只设置了"max-age"。问题是浏览器保留了上一次请求的旧"If-Modified-Since"值,因此始终检查资源并获取304响应。清除缓存后,新策略被应用,现在该文件如预期地直接从内存/磁盘缓存中提供服务。

1
确保首次Last-Modified时间正确的一个好理由是:不要伪造时间来规避问题。 - Jake

-2

有一条规则,将 Expires 头设置在超过一年的未来时间会违反HTTP 1.1 RFC

因此,这里的 HTTP 响应头是无效的(Expires: Tue, 19 Jan 2038 03:14:07 GMT)。修复此问题可能会解决该问题!


4
截至今年,RFC 2616已经过时,如https://tools.ietf.org/html/rfc2616顶部栏所示。我建议始终在https://tools.ietf.org上阅读RFC以检查是否过时,尽管在本例中W3也在http://www.w3.org/Protocols/rfc2616/rfc2616中注明了其过时性。缓存现在由https://tools.ietf.org/html/rfc7234涵盖,并且那里的更改附录指出“Expires头字段值的一年限制已被删除”。 - Mark Amery

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