在 S3 + Cloudfront 上以相同的 URL 托管多个 SPA Web 应用程序

Posted

技术标签:

【中文标题】在 S3 + Cloudfront 上以相同的 URL 托管多个 SPA Web 应用程序【英文标题】:Hosting multiple SPA web apps on S3 + Cloudfront under same URL 【发布时间】:2019-05-10 14:23:03 【问题描述】:

我有两个静态 Web 应用程序 (create-react-apps),它们当前位于两个单独的 S3 存储桶中。两个存储桶都配置为公共读取 + 静态 Web 托管,访问它们的 S3 托管 URL 可以正确显示站点。

Bucket 1 - First App:
   index.html
   static/js/main.js

Bucket 2 - Second App:
   /secondapp/
       index.html
       static/js/main.js

我为此设置了一个 Cloudfront - 默认 Cloudfront 源正确加载 FirstApp,因此 www.mywebsite.com 默认加载 index.html。

对于SecondApp,我设置了缓存行为,以便路径模式secondapp/* 指向SecondApp 存储桶URL。

在我的浏览器中,当我访问www.mywebsite.com/secondapp/ 时,它会正确显示第二个网络应用程序。

但是,如果我省略尾部斜杠,我会看到第一个应用程序,这是不受欢迎的。 如果我访问www.mywebsite.com/secondapp/something,我还会看到第一个应用程序,这也是不受欢迎的。 (我想让它加载secondapp的.html)

两个应用都配置为通过 react-router-dom 使用 html5 推送状态。

我希望的行为是访问以下内容会显示正确的站点/存储桶:

www.mywebsite.com - 目前正在工作

www.mywebsite.com/secondapp/ - 目前正在工作

www.mywebsite.com/secondapp - (没有斜杠)不工作,显示第一个应用程序

www.mywebsite.com/secondapp/something_else - 不工作,显示第一个应用程序

我怎样才能达到预期的行为?

谢谢!

【问题讨论】:

你的订单是什么? Precedence 0 是secondapp/* Precedence 1 是默认/* 您找到解决方案了吗?我有类似的情况。 @NrN 我做到了!我将在下面发布答案,但总之我使用了 lambda@edge 【参考方案1】:

在研究了这个问题后,我能够使用 lambda@edge (https://aws.amazon.com/lambda/edge/) 解决它

通过部署一个简单的 javascript 函数来将特定路径路由到所需的 s3 存储桶,我们能够实现类似 nginx 的路由设置。 该函数位于我们 Cloudfront CDN 上的 lambda@edge 上,这意味着您可以指定 何时 触发它。对我们来说,它在“原始请求”上

我的设置如下:

我使用单个 s3 存储桶,并将我的第二个应用程序部署在子文件夹“第二个应用程序”中 我创建了一个新的 Lambda 函数,托管在“U.S. East N Virginia”上。该区域在这里很重要,因为您只能在该区域中托管 lambda 函数和 @edge。 见下文了解实际的 Lambda 函数 创建后,转到您的 CloudFront 配置并转到“行为 > 选择默认 (*) 路径模式并点击编辑” 滚动到有“Lambda 函数关联”的底部 从下拉列表中选择“来源请求” 输入您的 lambda 函数的地址 (arn:aws:lambda:us-east-1:12345667890:function:my-function-name)

这是我使用的 lambda 函数的示例。


var path = require('path');

exports.handler = (event, context, callback) => 
  // Extract the request from the CloudFront event that is sent to Lambda@Edge
  var request = event.Records[0].cf.request;

  const parsedPath = path.parse(request.uri);

  // If there is no extension present, attempt to rewrite url
  if (parsedPath.ext === '') 
    // Extract the URI from the request
    var olduri = request.uri;

    // Match any '/' that occurs at the end of a URI. Replace it with a default index
    var newuri = olduri.replace(/second-app.*/, 'second-app/index.html');

    // Replace the received URI with the URI that includes the index page
    request.uri = newuri;
  
  // If an extension was not present, we are trying to load static access, so allow the request to proceed
  // Return to CloudFront
  return callback(null, request);
;

这些是我用于此解决方案的资源:

https://aws.amazon.com/blogs/compute/implementing-default-directory-indexes-in-amazon-s3-backed-amazon-cloudfront-origins-using-lambdaedge/ https://github.com/riboseinc/terraform-aws-s3-cloudfront-website/issues/1 How do you set a default root object for subdirectories for a statically hosted website on Cloudfront?

【讨论】:

这如何处理像/second-app/static/index.css这样的路径上的资产请求 他忽略了扩展名。 我目前正在做类似的事情;但是,这是否意味着从该域请求的每个包和 URL 都通过这个 lambda 请求?我担心在高负载下,lambda 边缘函数将成为瓶颈,只允许 1000 个并发(默认)请求。从那以后你还看到什么吗? 嗨。你能分享你的云前端发行版的配置吗?你声明了一个默认对象吗?您是否将路径模式指向静态 S3 URL?等等

以上是关于在 S3 + Cloudfront 上以相同的 URL 托管多个 SPA Web 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

AWS Amplify CLI CloudFront S3 限制

使用 S3/CloudFront 将 www 重定向到非 www,无需单独的存储桶

使用 .htaccess 从 Amazon S3 CloudFront 访问服务器文件

具有相同域名的 Webserver 和 Cloudfront 分发

CloudFront / S3 ETag:CloudFront 是不是可以在 CF TTL 过期之前发送更新的 S3 对象?

在 CloudFront 的账户之间共享 S3 存储桶