Gatsby public 文件夹结构与 S3 静态网站托管不兼容

Gatsby public folder structure doesn't play nicely with S3 static website hosting

所以,假设我有一个没有后端的简单 Gatsby 网站,只有两个页面的纯文本,indexabout-us

现在当我 运行 gatsby build 时,public 文件夹结构是这样的:

├── index.html
├── about-us
│   ├── index.html

问题是,这种结构不能很好地与 S3 配合使用,如果我向 mywebsite.com/about-us 发出请求,它实际上会 return 一个 404。启用静态托管的 S3 不会自动到 mywebsite.com/about-us/index.html 的路线,虽然如果我手动浏览到该页面它会工作,但我的路线是一场噩梦。

问题是,Gatsby 中是否有一些配置使其不生成这样的子文件夹?而只是在根文件夹中创建一个 about-us.html?

所以,我想实现以下目标:

├── index.html
├── about-us.html

我的 src/pages 结构如下:

├── index.tsx
├── about-us.tsx

你可以configure an index document。那么如果访问mywebsite.com/about-us,S3会先找一个对象about-us。如果未找到 about-us 对象,它将搜索索引文档 about-us/index.html.

有一个 gatsby plugin that kinda does what you are asking but make sure you are setting your canonical urls to prevent duplicate content 正在被搜索引擎记录。

这在 gatsby 和 next js 中很常见(启用尾部斜杠和静态导出)。他们按照您使用 /path/index.html 描述的方式静态构建站点,并通过 /path/ 路由到路径(注意尾部斜杠)。

最简单的完整非插件解决方案是将您的 S3 站点放在 CloudFront distribution 后面并添加一个 lambda 函数以将所有流量重定向到正确的路由。我已经 运行 在一些小网站上生产了这个,我的账单每月不到 1 美元。

这是 Gatsby 文档,但他们使用 CLI,该页面底部还有其他参考资料。

这里是关于如何使用 CloudFront 分发创建静态站点的AWS docs

安装好 CloudFront 后,您需要创建一个 lambda 函数以在后台将 /route/ 重定向到 /route/index.html,但在 URL 中显示 /route/并设置适当的权限。

  1. 转到 lambda 并使用节点 14.x.x
  2. 从头开始​​创建一个新函数
  3. 单击 index.js
  4. 旁边的图标
  5. 剪切并粘贴下面的代码并保存

Lambda 函数 - 该函数将非尾部斜杠 (/route) 重定向到尾部斜杠 (/route/),这与 /route/index.html 相同。此外,将 /route/index.html 重定向到 /route 以获得用户友好的 url。最后,用 301 响应告诉搜索引擎只有 /route/ 有效。

"use strict";

exports.handler = (event, _, callback) => {
  // Extract the request from the CloudFront event that is sent to Lambda@Edge
  let request = event.Records[0].cf.request;
  // Extract the URI from the request
  let oldUri = request.uri;
  // If URI is a file
  const isFile = /\/[^/]+\.[^/]+$/.test(oldUri);
  // If not a file request and does not end with / redirect to /
  if (!isFile && !oldUri.endsWith("/")) {
    return callback(null, {
      body: "",
      status: "301",
      statusDescription: "Moved Permanently",
      querystring: request.querystring,
      headers: {
        location: [
          {
            key: "Location",
            value: `${oldUri}/`,
          },
        ],
      },
    });
  }
  // Match any '/' that occurs at the end of a URI. Replace it with a default index
  request.uri = oldUri.replace(/\/$/, "/index.html");
  // Return to CloudFront
  return callback(null, request);
};

注意 - lambda 函数是从某个地方获取的,我为这种情况修改了一段时间,但我不记得它来自哪里 - 我只是从我的 lambda 中剪切了一个粘贴的 if。

完成上述操作后,您需要编辑 CloudFront 分配以接受 lambda 函数。

  1. 转到 CloudFront 并select您的分配
  2. Select 行为选项卡,select 默认行为,然后单击编辑按钮
  3. Lambda 函数关联设置 - CloudFront 事件到源请求 - 然后将 Lambda 函数 ARN 设置为您刚刚创建的函数。

最后,您需要 set the IAM permissions 以便您的发行版可以访问您的 lambda 函数。