Gatsby public 文件夹结构与 S3 静态网站托管不兼容
Gatsby public folder structure doesn't play nicely with S3 static website hosting
所以,假设我有一个没有后端的简单 Gatsby 网站,只有两个页面的纯文本,index
和 about-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/
并设置适当的权限。
- 转到 lambda 并使用节点 14.x.x
从头开始创建一个新函数
- 单击 index.js
旁边的图标
- 剪切并粘贴下面的代码并保存
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 函数。
- 转到 CloudFront 并select您的分配
- Select 行为选项卡,select 默认行为,然后单击编辑按钮
- Lambda 函数关联设置 - CloudFront 事件到源请求 - 然后将 Lambda 函数 ARN 设置为您刚刚创建的函数。
最后,您需要 set the IAM permissions 以便您的发行版可以访问您的 lambda 函数。
所以,假设我有一个没有后端的简单 Gatsby 网站,只有两个页面的纯文本,index
和 about-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/
并设置适当的权限。
- 转到 lambda 并使用节点 14.x.x 从头开始创建一个新函数
- 单击 index.js 旁边的图标
- 剪切并粘贴下面的代码并保存
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 函数。
- 转到 CloudFront 并select您的分配
- Select 行为选项卡,select 默认行为,然后单击编辑按钮
- Lambda 函数关联设置 - CloudFront 事件到源请求 - 然后将 Lambda 函数 ARN 设置为您刚刚创建的函数。
最后,您需要 set the IAM permissions 以便您的发行版可以访问您的 lambda 函数。