# 使用 AWS 的 CloudFront 和 Route 53 配置子目录

{% hint style="info" %}
本指南介绍如何使用 AWS CloudFront 和 Lambda\@Edge 设置子目录。这是 AWS 用户的一种实现方式。如果你使用的是不同的 AWS 配置（例如使用 EC2 实例运行 NGINX 的负载均衡器），你可能需要以不同方式配置反向代理。请联系 [支持](https://gitbook.com/docs/help-center/further-help/how-do-i-contact-support) 如果你需要其他配置方面的指导。
{% endhint %}

{% stepper %}
{% step %}

#### 配置你的 GitBook 网站

在你的 GitBook 组织中，点击侧边栏中的文档站点名称，然后点击 **管理站点** ，或者打开 **设置** 标签页。打开 **域名和重定向** 部分，在“子目录”下，点击 **设置子目录**.

输入你希望托管文档的 URL。然后指定文档访问的子目录，例如 `example.com/docs`，然后点击 **配置**.

在 **其他配置**，此时你会看到一个代理 URL。你将在下一步配置 Lambda 函数时使用它。请将其复制到剪贴板。
{% endstep %}

{% step %}

#### 创建你的 Lambda\@Edge 函数

登录到你的 AWS 控制台并导航到 **Lambda**.

点击 **创建函数** 按钮。

选择 **从头开始编写**，然后：

* 为你的函数指定一个描述性名称，例如 `gitbook-subpath-proxy。`
* 选择 **Node.js** 作为运行时（使用最新可用版本）。
* 保持架构和其他设置为默认值。

点击 **创建函数**.
{% endstep %}

{% step %}

#### 更新 Lambda 函数代码

在 Lambda 函数编辑器中，将默认代码替换为以下内容：

{% code lineNumbers="true" %}

```javascript
export const handler = async (event) => {
	const request = event.Records[0].cf.request;
	
	// 如果你的子目录不是 /docs，请更新此处
	const subdirectory = '/docs';
	
	// 使用下面你的代理 URL 更新此处
	const target = new URL('<来自 GitBook 的代理 URL>');

	// 重写：/docs* -> proxy.gitbook.site
	if (request.uri.startsWith(subdirectory)) {
		request.uri = target.pathname + request.uri.substring(subdirectory.length);

		// 如果存在，移除尾部斜杠
		if (request.uri.endsWith('/')) {
			request.uri = request.uri.slice(0, -1);
		}

		request.origin = {
			custom: {
				domainName: target.host,
				port: 443,
				protocol: 'https',
				path: '',
				sslProtocols: ['TLSv1.2'],
				readTimeout: 30,
				keepaliveTimeout: 5,
				customHeaders: {},
			},
		};

		request.headers['host'] = [{ key: 'host', value: target.host }];
		request.headers['x-forwarded-host'] = [{ key: 'x-forwarded-host', value: target.host }];
	}
    
	return request;
};
```

{% endcode %}

{% hint style="warning" %}
务必更新 `target` 第 8 行中的 GitBook 代理 URL。这看起来会像 `https://proxy.gitbook.site/sites/site_XXXX`
{% endhint %}

{% hint style="warning" %}
另外，如果你使用的子目录路径不是 `subdirectory` 第 5 行也要更新为你使用的不同子目录路径，例如 `/docs`.
{% endhint %}

点击 **Deploy** 以保存更改。
{% endstep %}

{% step %}

#### 为 Lambda\@Edge 配置 Lambda 权限

在将 Lambda 函数与 CloudFront 一起使用之前，你需要配置执行角色以允许 Lambda\@Edge 承担该角色。

1. 在你的 Lambda 函数中，点击 **配置** 标签页
2. 点击 **权限** 在左侧边栏中
3. 在 **执行角色**，点击角色名称以在 IAM 中打开它
4. 点击 **信任关系** 标签页
5. 点击 **编辑信任策略**
6. 将信任策略替换为以下内容：

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "edgelambda.amazonaws.com",
                    "lambda.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

点击 **更新策略** 以保存。
{% endstep %}

{% step %}

#### 发布你的 Lambda 函数

Lambda\@Edge 需要已发布的版本（而不只是 `$LATEST`).

1. 在你的 Lambda 函数中，点击右上角的 **操作** 下拉菜单
2. 选择 **发布新版本**
3. 可选地添加一个描述，例如“CloudFront 的初始版本”
4. 点击 **发布**
5. **重要：** 复制页面顶部显示的已发布版本 ARN（它将包含末尾的版本号，例如 `arn:aws:lambda:us-east-1:123456789:function:gitbook-subpath-proxy:1`)

{% hint style="warning" %}
Lambda\@Edge 函数必须创建在 **us-east-1** （美东 - 弗吉尼亚北部）区域。如果你在其他区域创建了函数，则需要在 us-east-1 重新创建它。
{% endhint %}
{% endstep %}

{% step %}

#### 创建你的 CloudFront 分配

导航到 **CloudFront** 在 AWS 控制台中并点击 **创建分配**.

配置以下设置。对于未指定的设置，请保持默认值。

**指定源站**

| 设置        | 值                          |
| --------- | -------------------------- |
| **源站类型**  | 其他                         |
| **自定义源站** | 你的主网站域名（例如， `example.com`) |

**缓存设置**

| 设置         | 值                         |
| ---------- | ------------------------- |
| **缓存策略**   | CachingDisabled           |
| **源站请求策略** | AllViewerExceptHostHeader |

点击 **接下来，** 选择你偏好的安全防护，然后再次点击 **下一步** 。

点击 **创建分配**.

等待分配部署完成（状态将从“进行中”变为“已启用”）。这可能需要几分钟。
{% endstep %}

{% step %}

#### 将 Lambda\@Edge 关联到 CloudFront

一旦你的 CloudFront 分配已部署：

1. 点击你的分配 ID 以打开其设置
2. 前往 **行为** 标签页
3. 选择默认行为并点击 **编辑**
4. 向下滚动到 **函数关联**
5. 在 **源站请求**，选择 **Lambda\@Edge**
6. 在 **Lambda 函数 ARN** 字段，粘贴你已发布的 Lambda 函数 ARN（来自第 5 步）
7. 勾选 **包含正文** 以允许函数在需要时访问请求正文
8. 点击 **保存更改**
   {% endstep %}

{% step %}

#### 配置域名和 DNS 记录

1. 在你的 CloudFront 分配主页上，点击 **常规** 标签页，并在 **备用域名**下，点击 **添加域名**
2. 输入你正在为其配置子目录的域名，例如 `example.com` 并点击 **下一步**
3. 选择你现有的 TLS 证书，或在需要时创建新的证书，然后点击 **下一步** 再次
   {% endstep %}

{% step %}

#### 从 CloudFront 配置 Route 53 DNS 记录

如果你使用 Route 53 作为 DNS，你需要创建或更新 DNS 记录，使其指向你的 CloudFront 分配。

1. 在 CloudFront 分配主页上保持不变，确保你位于 **常规** 标签页，然后在你在 **备用域名** 中配置的 URL 下方，点击 **将域名路由到 CloudFront。**
2. 点击 **自动设置路由** 为你的域名创建 A 和 AAAA DNS 记录

{% hint style="info" %}
如果你不使用 Route 53，则需要更新 DNS 提供商的设置，使你的域名指向 CloudFront 分配域名。你可以在 CloudFront 分配详情中的“分配域名”下找到它。
{% endhint %}
{% endstep %}

{% step %}

#### 测试你的配置

一旦所有更改传播完成（这可能需要 10–15 分钟）：

1. 打开浏览器并访问带有子目录路径的域名（例如， `https://example.com/docs`)
2. 你应该会看到你的 GitBook 文档站点！

如果站点没有立即加载，请尝试：

* 再等待几分钟以完成 DNS 传播
* 清除浏览器缓存或尝试无痕窗口
* 运行 `nslookup yourdomain.com` 在终端中验证 DNS 是否正确解析
* 检查 CloudFront 分配状态是否为“已启用”而不是“进行中”

{% hint style="success" %}
恭喜！你的 GitBook 文档现在可以通过你的自定义子目录访问了。
{% endhint %}
{% endstep %}
{% endstepper %}

### 故障排除

**Lambda 函数未触发：**

* 确保你已发布 Lambda 函数的版本（而不是使用 `$LATEST`)
* 确认 Lambda 函数位于 us-east-1 区域
* 检查信任策略是否包含 `edgelambda.amazonaws.com`

**DNS 未解析：**

* DNS 更改可能需要时间才能传播（最长可达 48 小时，但通常会快得多）
* 验证你的 Route 53 记录是否指向正确的 CloudFront 分配
* 检查你是否删除了任何旧的冲突 DNS 记录

**SSL 证书错误：**

* 确保你在 AWS Certificate Manager 中的 SSL 证书包含你的自定义域名
* CloudFront 的证书必须在 us-east-1 区域创建

**子目录不起作用：**

* 验证 `SUBDIRECTORY` 在你的 Lambda 函数中的值与你在 GitBook 中配置的值一致
* 检查 `target` 在你的 Lambda 函数中是否正确
* 查看 CloudFront 日志，确认请求是否到达了分配
