Lambda@Edge函数未在Cloudfront错误页面上调用

10
我有一个Angular应用程序的静态文件通过Cloudfront被存储在S3存储桶中进行服务。我的Cloudfront分配设置了错误页面,因此仍会渲染Angular的index.html。这意味着,如果我请求<cloudfront-distribution>.cloudfront.net/home-page,它不会说在S3存储桶中找不到名为home-page的文件,而是仍将渲染Angular应用程序,并且Angular应用程序将处理该/home-page路由。
我需要在应用程序服务器上包含一些安全标头,因此我设置了Lambda@Edge函数来在查看器响应事件上注入这些标头(如此处描述:https://aws.amazon.com/blogs/networking-and-content-delivery/adding-http-security-headers-using-lambdaedge-and-amazon-cloudfront/)。Lambda@Edge 对于实际对应 S3 存储桶中文件的路由有效(例如,如果我在 S3 存储桶的根目录下有一个名为 image.png 的文件,并请求 <cloudfront-distribution>.cloudfront.net/image.png,我会看到通过 Lambda@Edge 函数注入的响应标头。问题是访问不对应 S3 存储桶中文件的路由。如果我访问 <cloudfront-distribution>.cloudfront.net/home-page,S3 将返回 404,CloudFront 将处理 404 并根据错误页配置采取相应措施,即以 200 状态代码响应并呈现 index.html 文件。而当这种情况发生时,我没有看到通过 Lambda@Edge 函数注入的任何标头,而我的 Angular 应用程序的所有其他脚本文件都具有标头。

如何使所有响应都经过 Lambda@Edge 函数?


1
你能确认一下你的Lambda@Edge是否配置为在Origin Response事件上运行吗? - tyron
实际上不是这样的。Lambda@Edge 运行在查看器响应事件上。我尝试将其设置为源响应,但结果是没有任何标头(甚至不匹配 S3 存储桶上文件的路由)。 - Guilherme Cabral
3个回答

5

我刚经历了与此完全相同的情况。我发现AWS文档中的这个页面最有帮助:https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/lambda-cloudfront-trigger-events.html

虽然当前的答案对于这个问题来说肯定是正确的,但我有另一个限制可能值得讨论:

  • CloudFront函数仅在查看器请求/响应事件上触发;而不是可以由Origin或Viewer事件触发的Lambda@Edge函数。在我的情况下,我想继续使用CloudFront函数,因为它们更轻/更快/更便宜。

正如您所观察到的(并在文档中调用的那样),当出现源错误时,包括返回200的自定义错误页面时,查看器响应事件不会被触发。

选项#1-切换到源请求事件

如果您已经在使用Lambda@Edge并且不想更改任何内容,那么这可能是最简单的更改。

  • 请记住,这发生在CloudFront中的任何缓存之前,因此不会针对所有用例再次调用。所以可能不适用于所有用例。
  • 但是,这也可能是一件好事,因为它减少了Lambda执行次数,并且头应该包含在缓存中。

选项#2-停止使用自定义错误响应

对我来说,我选择了这条路。我需要为查看器请求事件创建第二个函数,重新编写不是专门针对s3资源的请求,以便React Router路径返回我的index.html。(类似于这样:https://dev59.com/olIH5IYBdhLWcg3wjfm4#60012469)。最后,我删除了CloudFront中现有的自定义错误响应。

  • 是的,这是另一个在每个资源请求上执行的函数,但是比lambda@edge调用便宜约1/6(免费层之后),我认为我仍然是赚了。
  • 从s3中发现实际的403/404's的好处-例如当用户尝试加载旧的webpack块和死链接时

1
非常感谢您的分享!非常有帮助! - Aaronius

2

基于@philarmour的答案,我将展示一个具体的解决方案示例。

在您的CloudFront Distribution中删除返回/index.html的自定义404页面。然后添加一个新的CloudFront Function(根据您的需要自定义以下代码)

var rootPaths = {
    css: 1,
    favicon: 1,
    fonts: 1,
    img: 1,
    js: 1,
    'robots.txt': 1,
};
function handler(event) {
    var firstPart = event.request.uri.split('/')[1];
    if (!(firstPart in rootPaths)) event.request.uri = '/index.html';
    return event.request;
}

将此函数与您的CloudFront分发关联在VIEWER_REQUEST上,不要忘记发布


非常感谢您的分享!非常有帮助! - Aaronius
谢谢!只有您的解决方案在 CloudFront 分发同时具有负载均衡器和 LB 的错误不得被错误页面更改时,才能正确处理 SPA 路由。 - Artem

0
在 Origin Response 上运行 Lambda@Edge 函数后,它可以正常工作。但是不要忘记在进行此更改后在 CloudFront 上运行无效化。此外,Lambda 只有在发送请求到 Origin 时才会执行,这可以节省费用。

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