在S3 + Cloudfront上进行浏览器缓存破坏

12

我在S3 + Cloudfront上托管一个静态网站。要重新部署,我会上传静态文件。

aws s3 sync static

并使用以下方法使cloudfront缓存失效:

aws cloudfront create-invalidation

在我更新这些资源后,推荐的强制浏览器获取这些新资源的方式是什么?问题在于浏览器正在缓存这些资源,用户正在获取旧(无效)版本的脚本、图像和样式。

2个回答

13

通常,您可以采取多个步骤来确保您的AWS CloudFront和S3设置在新部署时进行缓存破坏。

  • 确保使index.html(如果已缓存)失效
  • 对于静态资源,如JavaScript、CSS等,您可以使用查询参数或新文件名。

使用新文件名

<!doctype html>
<html lang="en">
  <head>
     <link href="styles.h2d1f722.css" rel="stylesheet" />
  </head>
  <body>
     <script type="text/javascript" src="scripts.cbe3c974.js"></script>
  </body>
</html>

你可以使用前端构建工具(如Webpack、Gulp等)生成新的文件名。

使用查询参数

<!doctype html>
<html lang="en">
  <head>
     <link href="styles.css?hash=h2d1f722" rel="stylesheet" />
  </head>
  <body>
     <script type="text/javascript" src="scripts.js?hash=cbe3c974"></script>
  </body>
</html>

设置查询参数时,请确保您已在CloudFront中启用了它(否则将返回文件的缓存响应)。

注意:比较这两种方法各有优缺点。使用相同的文件名,您可以使用S3本地版本控制对文件进行版本管理,而使用新名称则没有太多意义,因为新部署会为文件添加新名称。此外,使用新文件名会使S3存储桶混乱,除非您将旧文件移动到其他存储桶中或将其删除。

  • 请确保在index.html中使用适当的元标记值(Cache-Control、Expires和Pragma Headers)。

使用元标记

<!doctype html>
<html lang="en">
  <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 href="styles.css?hash=h2d1f722" rel="stylesheet" />
  </head>
  <body>
     <script type="text/javascript" src="scripts.js?hash=cbe3c974"></script>
  </body>
</html>

在这个例子中,它指示浏览器不要缓存。但是,您可以设置适当的值。

  • 为静态资产实践版本控制方案,即使向最终用户提供较旧版本的 index.html(从浏览器缓存),网页仍可使用旧资产(JS、CSS 等)加载而没有任何问题。

嗨@Ashan,我也在苦苦挣扎。 S3中的元数据属性怎么办?如果有了这些标头,它们是否仍然必要? - Carlos Delgado
那么CloudFront读取HTML文件内容以找出要应用到每个文件的缓存策略? - Carlos Delgado
2
@CarlosDelgado 很抱歉让你感到困惑(我已经删除了之前的评论)。有两个级别的缓存。1)在CloudFront Edge Cache上,2)在浏览器级别的缓存中。 对于1),CloudFront中的TTL配置适用,对于2),Index.html元标签和S3元数据(其中一些)适用。 - Ashan
1
嗨@Ashan,感谢澄清。我认为值得一提的是,在这里有更多的选项,它们都在这里得到了很好的解释:https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Expiration.html#expiration-individual-objects - Carlos Delgado

2

如果浏览器已经保存了缓存值,你就无法强制远程浏览器更新,除非进行手动干预。

你需要添加

script.something.js?buildid=someuniquereference

并且使CloudFront不缓存查询字符串参数。
您还可以包括filename.hash.js或filename.hash.html,并使用index.html /默认文档,通过缓存控制标头减少缓存时间。
这样,如果您进行任何更改,您可以更改该数字,客户端上的缓存也将被打破。
但是,一旦您发送了缓存标头,就无法远程清除浏览器上的客户端缓存。
希望这有所帮助。

@Will 在 script.something.js?buildid=someuniquereference 中,someuniquereference 会随着每次构建而改变,当您构建新的资产时,这将强制浏览器从 cloudfront 重新加载资产,并且由于您已经创建了无效化,cloudfront 将提供新内容。 - Shubham Bansal
是的。通过参数进行缓存破坏。 - Kannaiyan
1
请注意,当执行此操作时,官方解决方案是在您希望S3忽略的查询字符串参数上使用“x-”前缀,即使其API引入了新功能。 有些前缀会被服务错误解释,例如“标记”和“选择”,因为它假定您正在尝试访问子资源而不是对象... 因此,例如“x-buildid = ...”可能是一种更具未来性的任意键选择。 - Michael - sqlbot
@Michael-sqlbot,你有一个解释这些的链接吗? - Kannaiyan
据我理解,如果我使用查询字符串方法,就不需要使文件失效,对吗? - Carlos Delgado
显示剩余2条评论

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