是文件名还是整个 URL 用作浏览器缓存中的键?

Posted

技术标签:

【中文标题】是文件名还是整个 URL 用作浏览器缓存中的键?【英文标题】:Is it the filename or the whole URL used as a key in browser caches? 【发布时间】:2010-09-10 04:25:57 【问题描述】:

通常希望浏览器缓存资源 - javascript、CSS、图像等,直到有新版本可用,然后确保浏览器获取并缓存新版本。

一种解决方案是在资源的文件名中嵌入版本号,但是以这种方式将要管理的资源放置在其中包含修订号的目录中会做同样的事情吗?文件的整个 URL 是用作浏览器缓存中的键,还是只是文件名本身和一些元数据?

如果我的代码从获取 /r20/example.js 更改为 /r21/example.js,我可以确定 example.js 的修订版 20 已被缓存,但现在已获取修订版 21 并且现在已缓存?

【问题讨论】:

【参考方案1】:

是的,URL 任何部分的任何更改(不包括 HTTP 和 HTTPS 协议更改)都会被浏览器(以及任何中间代理)解释为不同的资源,因此会导致浏览器缓存中的单独实体。

更新:

this ThinkVitamin article 中关于 Opera 和 Safari/Webkit 浏览器不缓存带有 ?query=strings 的 URL 的声明是false

向 URL 添加版本号参数是一种完全可以接受的缓存清除方法。

可能让 ThinkVitamin 文章的作者感到困惑的是,在 Safari 和 Opera 的地址/位置栏中按 Enter 会导致带有查询字符串的 URL 出现不同的行为。

但是,(这是重要的部分!)Opera 和 Safari 在缓存嵌入/链接的图像以及样式表和脚本时表现得与 IE 和 Firefox 相似在网页中——不管它们是否有“?” URL 中的字符。 (这可以通过在普通 Apache 服务器上的简单测试来验证。)

(如果我有声望,我会评论当前接受的答案。:-)

【讨论】:

我需要重新检查当前的浏览器,但我可以确认过去只更改 URL 中的参数并不能保证缓存会被破坏。请记住,我的答案也是近 3 年前添加的……从那以后发生了很多变化。 我上面的回答也是3年前的。我当时做了一个彻底的测试,发现 Safari 和 Opera 都表现得很好,我松了一口气。然而,对此进行测试很困难/令人困惑,因为 Safari 和 Opera 在重新加载页面(或在位置栏中按 Enter 键)时倾向于忽略链接资源上的缓存指令,而不是通过单击链接访问它们。 顺便说一句,“仅更改 URL 中的参数并不能保证缓存会被破坏”是一个奇怪的说法。我以前从未见过有人建议过这个。只是相反(就像现在丢失的 ThinkVitamin 文章中一样),浏览器不缓存带有参数的页面(它们仍然这样做)。 我在上面的原始答案中添加了评论。诀窍在于,有数十种浏览器(桌面和移动)、代理和使用 Web 内容的软件应用程序。因为至少从历史上看,不可能通过 url 参数依赖缓存清除,所以我早就使用了文件重命名技术。 我无法想象浏览器、mozilla、netscape 等历史上的任何缓存在派生缓存键时都会忽略查询字符串。查询字符串的全部意图一直是更改 http 响应并忽略它会呈现无用的缓存。【参考方案2】:

浏览器缓存键是请求方法和资源 URI 的组合。 URI 由方案、权限、路径、查询和片段组成。

来自HTTP 1.1 specification的相关摘录:

主缓存键由请求方法和目标 URI 组成。 但是,由于当今常用的 HTTP 缓存通常是有限的 为了缓存对 GET 的响应,许多缓存只是拒绝其他方法 并且仅使用 URI 作为主缓存键。

来自URI specification的相关摘录:

通用 URI 语法由一个分层序列组成 称为方案、权限、路径、查询和 片段。

URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
hier-part   = "//" authority path-abempty
              / path-absolute
              / path-rootless
              / path-empty

【讨论】:

确保#fragment 部分永远不会用作缓存键。无论如何,它不存在于 HTTP 请求中。这是浏览器功能的一部分,而不是 HTTP。【参考方案3】:

我 99.99999% 确定它是用于在浏览器中缓存资源的整个 url,因此您的 url 方案应该可以正常工作。

【讨论】:

嗯...除了#fragment 部分。【参考方案4】:

标识 HTTP 对象所需的最小值是完整路径,包括任何查询字符串参数。一些浏览器可能不会缓存带有查询字符串的对象,但这与缓存的键无关。

同样重要的是要记住路径不再足够。 HTTP 响应中的 Vary: 标头会提醒浏览器(或代理服务器等)除 URL 之外的任何其他内容,应用于确定缓存键,例如 cookie、编码值等。

对于您的基本问题,是的,更改 .js 文件的 URL 就足够了。对于决定缓存键的更大问题,它是 URL 加上 Vary: 标头限制。

【讨论】:

【参考方案5】:

是的。从缓存的角度来看,不同的路径是相同的。

【讨论】:

【参考方案6】:

当然,它必须使用整个路径“/r20/example.js”与“/r21/example.js”可能是完全不同的图像开始。您的建议是处理版本控制的可行方法。

【讨论】:

【参考方案7】:

取决于。它应该是完整的 URL,但某些浏览器(Opera、Safari2)对具有不同参数的 url 应用不同的缓存策略。

最好的办法是更改文件名

这里有一个非常聪明的解决方案(使用php、Apache)

http://verens.com/archives/2008/04/09/javascript-cache-problem-solved/

策略说明: “根据 HTTP 缓存规范的规定,用户代理不应该缓存带有查询字符串的 URL。虽然 Internet Explorer 和 Firefox 会忽略这一点,但 Opera 和 Safari 不会——为了确保所有用户代理都可以缓存您的资源,我们需要将查询字符串排除在其 URL 之外。”

http://www.thinkvitamin.com/features/webapps/serving-javascript-fast

【讨论】:

ThinkVitamin.com 的文章是错误的。 Safari 和 Opera do 使用查询字符串缓存 URL,就像任何 URL 一样。 (See my answer 了解更多信息) 除了浏览器之外,一些流行的代理需要更改文件名才能破坏缓存。 stevesouders.com/blog/2008/08/23/… 在这篇文章中提到了 Squid 代理服务器(它确实在 2.7 版中更改了它的默认值)我宁愿安全地使用它并更改文件名。 恐怕您要么读错了 Steve Sounders 的帖子,要么打错了字。 Steve 的发现是他的 Squid 代理没有缓存查询字符串 URL - 这与“要求更改文件名以破坏缓存”完全相反。 关于“是文件名还是整个 URL 用作浏览器缓存中的键?”的问题。那么答案就是简单的“是”。一些(通常是过时的中间代理)安全地运行它并且不会不缓存某些类型的 URL 是一个小烦恼,并且不会改变整个 URL 被用作所有 Web 中的键的基本事实chaches。 只是为了后代:我觉得“用户代理不应该缓存带有查询字符串的 URL” 是 missing a critical part from RFC2616:“因为一些应用程序传统上使用 GET 和带有查询 URL 的 HEAD [...] 执行具有显着副作用的操作,缓存不得将对此类 URI 的响应视为新鲜的除非服务器提供明确的到期时间(强调我的)。【参考方案8】:

整个网址。我在一些区分大小写的旧浏览器中看到了一种奇怪的行为。

【讨论】:

【参考方案9】:

除了现有的答案,我只想补充一点,如果您使用 ServiceWorkers 或离线插件,它可能不适用。然后,您可能会遇到不同的缓存规则,具体取决于 ServiceWorker 的设置方式。

【讨论】:

【参考方案10】:

在大多数浏览器中使用完整的 url。 在某些浏览器中,如果您在 url 中有查询,则该文档将永远不会被缓存。

【讨论】:

以上是关于是文件名还是整个 URL 用作浏览器缓存中的键?的主要内容,如果未能解决你的问题,请参考以下文章

从输入 URL 到浏览器接收的过程中发生了什么事情?

请求响应流程

从输入一个URL地址到浏览器完成整个渲染的过程

缓存控制的默认值是多少?

我设计的js禁止浏览器回退,在别的浏览器上可以,但在uc上失效

浏览器缓存系列之一:基础知识必备