通过参数清除缓存


122

我们希望在生产部署中缓存崩溃,但不要浪费大量时间来弄清楚这样做的系统。我的想法是在当前版本号的css和js文件的末尾应用参数:

<link rel="stylesheet" href="base_url.com/file.css?v=1.123"/>

两个问题:这会有效地打破缓存吗?由于参数表明这是动态内容,因此该参数会导致浏览器从不缓存该URL的响应吗?

Answers:


115

参数?v=1.123表示查询字符串,因此浏览器将认为这是从到的新路径?v=1.0。因此导致它从文件而不是从缓存加载。如你所愿。

并且,浏览器将假定下次调用时源将保持不变,?v=1.123使用该字符串对其进行缓存。因此它将保留在缓存中,但是服务器已设置,直到您移动到?v=1.124诸如此类。


4
引用史蒂夫·苏德斯(Steve Souders)的话:“要获得流行代理缓存的好处,请避免使用查询字符串进行修改,而应修改文件名本身。” 完整的解释可以在这里找到:stevesouders.com/blog/2008/08/23/…–
lao

25
那篇博客文章现在已经接近十年了。您认为缓存提供程序和CDN尚未适应吗?鱿鱼似乎能够缓存与查询字符串的文件现在
jeteon

1
也许这对某人有帮助:就我个人而言,我将文件修改时间戳记用作“自动”版本参数,例如。<link rel="stylesheet" href="style.css?v=1487935578" />
oelna '17

我个人不明白为什么,但是Lara Hogan(Swanson)(Etsy的工程经理)不建议使用查询参数来消除缓存。我认为这与用户和服务器之间的缓存代理有关。
山姆·鲁比

36

两个问题:这会有效地打破缓存吗?

是。甚至Stack Overflow都使用这种方法,尽管我记得他们(每天有数百万的访问者,以及数以百万计的不同客户端和代理版本和配置)也有一些异常情况,即使这样也不足以破坏缓存。但是一般的假设是这将起作用,并且是打破客户端缓存的合适方法。

由于参数表明这是动态内容,因此该参数会导致浏览器从不缓存该URL的响应吗?

否。该参数将不会更改缓存策略;它不会更改缓存策略。服务器发送的缓存头仍然适用,如果不发送,则为浏览器的默认值。


1
@spender我现在找不到参考文献,恐怕是一篇冗长的博客文章或杰夫·阿特伍德(Jeff Atwood)谈论它的答案(IIRC)
Pekka

2
@spender我已经读到一些代理服务器(旧的或可以配置为)在缓存时会忽略查询字符串。
MrWhite

2
@spender-我也听到过同样的话,我认为更改文件名或路径是最佳选择。这可能是最简单的只是让移动所有的静态文件下一个版本的文件夹名称,例如/static/v22/file.css,你可以做一个单一的文件夹重命名,例如多个文件 /static/v23/file.css/static/v23/mystuff.js
布拉德·帕克斯

22

将版本号放在实际文件名中比较安全。这允许一次存在多个版本,因此您可以推出一个新版本,如果仍然存在请求较早版本的缓存HTML页面,则它们将获得与其HTML兼容的版本。

注意,在Internet上最大的版本化部署之一中,jQuery使用实际文件名中的版本号,并且可以安全地允许多个版本共存,而无需任何特殊的服务器端逻辑(每个版本只是一个不同的文件)。

当您部署新页面和新链接文件(这就是您想要的)时,这将破坏一次缓存,然后可以有效地缓存(您也想要的)那些版本。


我同意这一点,但是仅将Sinatra附加?v = <%= VERSION%>附加到所有CSS和JS请求要容易得多,而不必单独控制每个文件。最终,我们将切换到sinatra-assetpack,它将对所有文件进行预处理和压缩,并在文件名中实际附加一个版本号,这将使我们更轻松地分别控制它们。
布拉德·赫曼

1
如果您要确保10000%的确定性,我同意将版本号放在文件名中是最安全的最终解决方案,但是我不遵循“一次存在多个版本”的说法。带有查询参数的URL与带有不同查询参数的相同URL不同。客户应将它们视为两种不同的资源;如果不是,则说明客户端已损坏。
Pekka 2012年

2
@Pekka-版本号可以允许一次存在多个版本,但这需要服务器合作才能将查询参数映射到正确的实际文件。我不认为这是OP在这里所做的事情,并且没有理由要求修改文件名时的复杂性要简单得多,并且不需要服务器的配合。显然两者都可以。
jfriend00 2012年

11

正如其他人所说,使用查询参数进行缓存清除通常被认为是一个坏主意(tm),并且已经存在了很长时间。最好在文件名中反映版本。建议不要使用Html5 Boilerplate等查询字符串。

也就是说,在我看到的引用了消息来源的建议中,所有建议似乎都取材于Steve Souders 在2008年的一篇文章中。他的结论基于当时的代理行为,这些天可能相关也可能不相关。但是,在没有更多最新信息的情况下,更改文件名是安全的选择。


9

它将下载一次缓存,客户端下载完资源后,将从客户端缓存中获取其他所有响应,除非:

  1. v参数已更新。
  2. 客户端清除其缓存

6

通常,这应该没问题,但是如果存在一个配置为忽略请求参数的中间缓存(代理),则可能无法正常工作。

例如,如果您正在通过Akamai CDN提供静态内容,则可以将其配置为忽略请求参数,以防止使用此方法来破坏缓存。


5

这在很大程度上取决于您希望缓存具有多强的功能。例如,鱿鱼代理服务器(可能还有其他)默认情况下缓存带有查询字符串的URL-至少在撰写该文章时确实如此。如果您不介意某些用例会导致不必要的缓存未命中,请继续使用查询参数。但是,设置基于文件名的缓存清除方案非常容易,这样可以避免此问题。


5
Steve Souders文章中引用的鱿鱼代理更改了其默认缓存策略。从2.7版(2008年5月)和3.1版(2010年3月)开始,默认行为是缓存动态内容。
Josh Rack 2015年

5

发现2项技术的比较(查询字符串VS文件名)在这里

版本作为查询字符串有两个问题。

首先,它可能并不总是一个浏览器,它实现了我们需要破坏的缓存。据说某些(可能是较旧的)代理在缓存行为方面确实忽略了查询字符串。

其次,在某些更复杂的部署方案中,如果您有多个前端和/或多个后端服务器,则升级不是即时的。您需要能够同时服务新旧版本的资产。例如,查看使用Google App Engine时这对您的影响。


4

另一种类 似的方法是在提供文件时使用htaccess mod_rewrite忽略部分路径。您永不缓存的索引页引用了文件的最新路径。

从开发角度来看,使用参数作为版本号是一样容易,但它与文件名方法一样强大。

使用路径的被忽略部分作为版本号,服务器将忽略它并提供未缓存的文件。

1.2.3/css/styles.csscss/styles.css由于htaccess文件剥离并忽略了第一个目录,因此提供的文件相同

包括版本文件

<?php
  $version = "1.2.3";
?>

<html>
  <head>
    <meta http-equiv="cache-control" content="max-age=0" />
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="0" />
    <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
    <meta http-equiv="pragma" content="no-cache" />
    <link rel="stylesheet" type="text/css" href="<?php echo $version ?>/css/styles.css">
  </head>
  <body>
    <script src="<?php echo $version ?>/js/main.js"></script>
  </body>
</html>

请注意,这种方法意味着您需要禁用索引页的缓存- 使用<meta>标记来关闭所有浏览器的缓存?

.htaccess文件

RewriteEngine On

# if you're requesting a file that exists, do nothing
RewriteCond %{REQUEST_FILENAME} !-f 
# likewise if a directory that exists, do nothing
RewriteCond %{REQUEST_FILENAME} !-d 

# otherwise, rewrite foo/bar/baz to bar/baz - ignore the first directory
RewriteRule ^[^/]+/(.+)$ $1 [L] 

您可以在允许URL重写的任何服务器平台上采用相同的方法

(重写条件改编自mod_rewrite-将目录重写为查询字符串,但/#!/除外

...,如果您的索引页/站点入口点需要缓存清除,则可以始终使用JavaSript刷新它。


2
<script type="text/javascript">
// front end cache bust

var cacheBust = ['js/StrUtil.js', 'js/protos.common.js', 'js/conf.js', 'bootstrap_ECP/js/init.js'];   
for (i=0; i < cacheBust.length; i++){
     var el = document.createElement('script');
     el.src = cacheBust[i]+"?v=" + Math.random();
     document.getElementsByTagName('head')[0].appendChild(el);
}
</script> 

在开发/测试新版本期间,缓存可能会成为问题,因为浏览器,服务器甚至3G电信公司(如果您进行移动部署)有时会缓存静态内容(例如JS,CSS,HTML,img)。您可以通过在URL上附加版本号,随机数或时间戳来克服此问题,例如:JSP:<script src =“ js / excel.js?time = <%= new java.util.Date()%>”> </脚本>如果您运行的是纯HTML(而不是服务器页面JSP,ASP,PHP),则服务器将无济于事。在浏览器中,链接了JS运行前加载的,所以你必须与JS删除链接并加载它们
Conete克里斯蒂安

我不认为这会按顺序同步加载JS文件。
Stealth Rabbi

0
 <script>
    var storedSrcElements = [
         "js/exampleFile.js",
         "js/sampleFile.js",
         "css/style.css"
          ];

    var head= document.getElementsByTagName('head')[0];
    var script;
    var link;
    var versionNumberNew = 4.6;

    for(i=0;i<storedSrcElements.length;i++){
     script= document.createElement('script');
     script.type= 'text/javascript';
     script.src= storedSrcElements[i] + "?" + versionNumberNew;
     head.appendChild(script);
    }     


     </script> 


       ### Change the version number  (versionNumberNew) when you want the new files to be loaded  ###

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.