如何在 SPA 上解决 "x-cache: Error from cloudfront"

How to solve "x-cache: Error from cloudfront" on SPA

我们在尝试使 SPA 与客户端路由器(反应路由器)一起工作时遇到问题。我们使用有一个 DOMAIN -> CDN (CloudFront) -> S3 的概念来为我们的静态文件提供服务。

我们已将 S3 配置为提供静态文件。 CDN 配置为来自 S3,我们配置了自定义错误页面以捕获错误:

使用此配置我们可以捕获如下错误:

https://www.example.com/custom-url

CDN 会将所有 404/403 错误重定向到主要 index.htmlreact router 将获得正确的路由。

我们的站点正在运行,客户端路由器工作正常,但我们的 CDN 响应有问题 x-cache: Error from cloudfront:

如果我们在没有任何查询参数(不是查询字符串)的情况下访问主 url https://www.example.com 一切正常。

我怎样才能解决这个问题并使我的所有动态 URL 都能正常工作?

谢谢。

Amazon CloudFront 的配置和调试错误代码可能会很棘手 - 将其追溯到根本原因可能需要几个小时。但是,在进入日志之前,您可能需要进行以下检查。


存储桶名称!

存储桶名称应与您托管的网站名称相匹配。

For instance, to host your-domain.com website on Amazon S3, you would create a bucket named your-domain.com.

To host a website under www.your-domain.com, you would name the bucket www.your-domain.com.

最好的做法是为您的-domain.com 和www.your-domain.com 创建存储桶。

将您 settings/configuration 中的现有逻辑用于这些存储桶中的任何一个,并使用它来为静态网站提供服务。使用另一个桶将请求重定向到原始桶。

老实说,这不会给您带来麻烦,因为您已经将您的系统与 Amazon CloudFront 集成,它可以配置为使用任何名称的 Amazon S3 存储桶。

使用 Amazon CloudFront,访问您的域的用户将直接从 CloudFront 分配中获取数据,而 CloudFront 分配又从我们的 S3 存储桶中缓存内容。


配置分发的来源设置。

在使用 Amazon CloudFront 创建分配时,记下关联的 Amazon S3 终端节点和源域名。确保使用网站端点并且不是 REST 端点。不要使用 CloudFront 自动建议的端点。

Amazon Web Services official documentation

中解释的行为存在差异

4XX 错误代码!

根据您的控制台日志,它表明分发实例正在尝试访问禁止的元素、页面或资源,因此出现 403 状态代码。

而 404 只是页面未找到的结果。但是,在错误重定向之后 - 正如您的配置中所处理的那样,用户被重定向回遇到 403.

index.html

有关 - How CloudFront Processes and Caches HTTP 4xx and 5xx Status Codes from Your Origin

的更多信息

其他常见的可疑对象包括 Amazon CloudFront 分发的缓存配置、AWS Route53 设置和 Amazon Certificate Manager。

如开头所述,在跟踪此类错误时可能会非常令人困惑。让我们知道以上内容是否有帮助。另外,如果你能 post 更新你的调查和发现,我将不胜感激。

感谢阅读。

当您访问 http://mywebsite.com 时,请求将命中 S3 中的 index.html 文件。然后您可以单击一个按钮并转到 http://mywebsite.com/stats,这是您的 SPA 应用程序的内部路由。因此,它不会触发任何后端请求。

但是如果您重新加载页面,http://mywebsite.com/stats 将被发送到 S3,因为您的浏览器不知道您是 运行 SPA 前端。

S3 将 return 403 错误 index.html 并且 Cloudfront 会将错误发送给您。

解决方案是在 Cloudfront 中使用边缘 lambda 函数。举个例子:

const path = require('path')

exports.handler = (evt, ctx, cb) => {
    const {request} = evt.Records[0].cf

    if (!path.extname(request.uri)) {
        request.uri = '/index.html'
    }

    cb(null, request)
}

来源:https://hackernoon.com/how-to-host-a-single-page-application-with-aws-cloudfront-and-lambda-edge-39ce7b036da2