如何使 CloudFront 永远不会在 S3 存储桶上缓存 index.html

Posted

技术标签:

【中文标题】如何使 CloudFront 永远不会在 S3 存储桶上缓存 index.html【英文标题】:How to make CloudFront never cache index.html on S3 bucket 【发布时间】:2018-01-25 10:00:23 【问题描述】:

我有一个托管在 S3 存储桶上的 React 应用程序。代码使用yarn build(它是一个基于 create-react-app 的应用程序)进行了缩小。 build 文件夹看起来像:

build
├── asset-manifest.json
├── favicon.ico
├── images
│   ├── map-background.png
│   └── robot-icon.svg
├── index.html
├── js
│   ├── fontawesome.js
│   ├── packs
│   │   ├── brands.js
│   │   ├── light.js
│   │   ├── regular.js
│   │   └── solid.js
│   └── README.md
├── service-worker.js
└── static
    ├── css
    │   ├── main.bf27c1d9.css
    │   └── main.bf27c1d9.css.map
    └── js
        ├── main.8d11d7ab.js
        └── main.8d11d7ab.js.map

我从不希望index.html 被缓存,因为如果我更新代码(导致main.*.js 中的十六进制后缀更新),我需要用户下次访问以了解<script src> 中的@987654327 更改@ 指向更新后的代码。

在 CloudFront 中,我似乎只能排除路径,而排除“/”似乎无法正常工作。我在更改代码时遇到了奇怪的行为,如果我点击刷新,我会看到它,但如果我退出 Chrome 并返回,我会看到由于某种原因非常过时的代码。

我不想在每次发布代码时都触发失效(通过 CodeBuild)。还有其他方法吗?我认为其中一个挑战是,由于这是一个使用 React Router 的应用程序,我不得不通过将错误文档设置为 index.html 并强制 HTTP 状态 200 而不是 403 来做一些诡计。

【问题讨论】:

【参考方案1】:

基于 CloudFront 配置的解决方案:

转到您的 CloudFront 分配,在“行为”选项卡下创建一个新行为。 指定以下值:

路径模式:index.html 对象缓存:自定义 最大 TTL:0(或另一个非常小的值) 默认 TTL:0(或另一个非常小的值)

保存此配置。

CloudFront 将不再缓存 index.html

【讨论】:

嗨 @seza443 这对内部目录中的 index.html 文件有效,还是仅适用于根目录中的文件? 感谢您的反馈。我可以确认初始规则 (index.html) 适用于内部目录。我检查了云端文档,基本上,它们的规则匹配文件名,即使在内部目录中,而不仅仅是根目录。 如果其他人好奇:从 AWS CLI 实现这一点不是很方便。 如果您使用 CI/CD 解决方案并且您的 index.html 每次都会更新,这就是您要走的路。恕我直言,它应该被接受为正确答案。 我认为这个答案适用于Cache and origin request settings 中的Use legacy cache settings 选项。如果您想通过选择Use a cache policy and origin request policy 选项来使用现有的托管策略,我认为正确的值是Managed-CachingDisabled【参考方案2】:

如果您不想缓存index.html,请仅在该文件上设置Cache-Control: max-age=0 标头。 CloudFront 会在每个请求上向您的原始 S3 存储桶发出请求,但听起来这是理想的行为。

如果您想要设置更长的到期时间并手动使 CloudFront 缓存失效,您可以使用 */* 作为您的失效路径(而不是您提到的 /)。但是,全球所有 CloudFront 边缘节点最多可能需要 15 分钟才能反映源中的更改。

【讨论】:

您是说在 S3 对象元数据中添加该标头吗?并且正确,无论 URL 路径如何,我都不想缓存 index.html。我比较关心缓存相关文件(JS文件、CSS、图片)。 是的:它在 AWS 文档中被称为“系统定义的元数据”:docs.aws.amazon.com/AmazonS3/latest/user-guide/… 太好了,这很有帮助!我已将部署过程设置为在复制索引文件时使用aws s3 cp --cache-control max-age=0。像魅力一样工作。 我知道这些是旧的问答,但我通过为 index.html 设置 Cache-Control: no-store 标头(作为 S3 文件中的元数据)实现了同样的目的。【参考方案3】:

这是我在将新文件上传到 s3 并使 Cloudfront 无效后运行以在我的 index.html 文件上设置缓存控制的命令:

aws s3 cp s3://bucket/index.html s3://bucket/index.html --metadata-directive REPLACE --cache-control max-age=0 --content-type "text/html"

【讨论】:

救生技巧。 谢谢!但是,我需要添加 --region eu-central-1 才能使其工作。 :) 谢谢!我还必须添加 -content-type "text/html" 选项,否则我的 index.html 文件将作为二进制文件提供。【参考方案4】:

在每个版本中运行 index.html 的失效要比每次都破坏 Cloudfront 的目的并从 S3 提供它(基本上是您的应用程序的入口点)要好得多。

【讨论】:

问题是一旦浏览器看到一个 index.html 没有缓存控制标头设置为不缓存,那么浏览器甚至不会在后续调用中转到服务器。因此,Cloudfront 失效不会为现有用户做任何事情。 @UğurDinç “问题是一旦浏览器看到 index.html 没有设置缓存控制标头以不缓存,那么浏览器甚至不会在后续调用中转到服务器。”至少 Chrome 确实如此。尽管如此,为了使其更具确定性,您可以将标头添加到 S3 对象。例如。如果您设置最小 TTL > 0 且大于 max-age,Cloudfront 将缓存对象以获取 TTL 的值(而浏览器将使用 max-age)。见:docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/… 我们发现在使用失效时并非所有边缘位置都被清除。我们希望将 index.html 标记为未验证(我们在 Angular 中构建了一个 SPA,并注意到一些用户正在获取旧版本)。 @jackofallcode 我最近观察到同样的问题,你有没有找到解决这个问题的方法? @asliwinski 我们已选择在 CloudFront 分配上设置缓存行为,以不缓存 index.html。使用 Angular,它唯一的小型和主要核心应用程序(JS 和 CSS)按预期缓存(并且新构建创建新文件而不是更新现有文件)。不理想,但一种解决方法。

以上是关于如何使 CloudFront 永远不会在 S3 存储桶上缓存 index.html的主要内容,如果未能解决你的问题,请参考以下文章

markdown s3 CloudFront清理缓存,使对象失效(仅Web分配)

如何使用特定的引用策略和 CloudFront 分配改进 S3 存储桶的反抓取

S3 + Cloudfront 上的浏览​​器缓存破坏

Amazon S3 + CloudFront 查询特定版本的存储文件

将 Amazon S3 存储桶和 Cloudfront 与 SSL Wordpress 站点一起使用不会提供静态文件

Amazon S3 是不是向 CloudFront 发送失效信号?