如何将子域名作为子目录提供服务

Avatar of John Siciliano
John Siciliano

DigitalOcean 为您的旅程的每个阶段提供云产品。立即开始使用 $200 免费赠送积分!

假设您有一个在设计方面表现出色的平台上构建的网站,该网站在 example.com 上可用。但是该平台在博客方面表现不佳。所以你想,“如果我能使用不同的博客平台并在 example.com/blog 上提供它怎么办?”

大多数人会告诉你,这违反了 DNS 和网站的工作原理,应该使用子域名而不是子目录。但是将内容保留在根域上有一些优点,这些优点是我们使用子域名无法获得的。

有一种方法可以在同一个 URL 上提供两个不同的平台。我将向您展示秘诀,这样,到本文结束时,我们将使 blog.example.com 作为 example.com/blog 提供服务。

为什么要这样做

因为您在这里,您可能已经知道为什么这是一个值得追求的道路。但我想确保您来到这里是为了做这件事的首要原因:SEO。查看这些 14 个案例研究,这些案例研究表明当人们将子域名迁移到子目录时会产生积极的效果。您希望您的博客和域共享 SEO 价值。将其放在子域名上会在一定程度上将两者断开连接。

这是我的原因,最终合并了两个平台,主域在 WordPress 上,子域在 Drupal 上。但是本教程与平台无关——它适用于几乎所有平台。

也就是说,我们将在本教程中介绍的 Cloudflare 方法 与 Shopify 不兼容,除非您为 Cloudflare 的企业版付费。这是因为 Shopify 也使用 Cloudflare,并且不允许我们在其免费定价层上代理流量。

步骤 0(预览)

在我开始之前,我想解释一下即将发生的事情的总体情况。简而言之,我们将有两个网站:我们的主网站(example.com)和子域名(blog.example.com)。我使用“blog”作为示例,但在我的情况下,我需要使用不同类型的 Drupal 内容。但是博客是典型的用例。

这种方法依赖于使用 Cloudflare 进行 DNS 和一些额外的操作,这些操作将提供魔法。我们将告诉 Cloudflare,当有人访问 example.com/blog 时,它应该

  1. 拦截该请求(因为 example.com/blog 并不真正存在),
  2. 在后台请求另一个域(blog.example.com/blog),并且
  3. 将最后一步的结果传递给访问者,通过 example.com/blog 掩盖。

好的,让我们更详细地了解一下!

步骤 1:使用 Cloudflare

再说一次,我们正在使用 Cloudflare 进行 DNS。将您的域的 DNS 指向那里是开始的第一步。

使用 Cloudflare 的原因是,它允许我们创建 Workers,这些 Workers 能够在有人访问某些 URL(称为 Routes,我们将在步骤 3 中创建)时运行一小段代码。这段代码将负责在后台切换网站。

Cloudflare 有一个关于 入门 的出色指南。目标是将您的域(无论其在哪里注册)指向 Cloudflare 的名称服务器,并确认 Cloudflare 在您的 Cloudflare 帐户中已连接。

步骤 2:创建 Worker

这段代码将负责在后台切换网站。转到 Workers,然后单击 创建服务

注意平均 CPU 时间!此过程将请求增加了大约 .7ms(所以基本上没有影响)。

命名您的服务,然后选择“HTTP 处理程序”。

单击 创建服务,然后单击 快速编辑

粘贴以下代码,并将第 16 行上的域名替换为您自己的域名

// Listen for every request and respond with our function.
// Note, this will only run on the routes configured in Cloudflare.
addEventListener('fetch', function(event) {
  event.respondWith(handleRequest(event.request))
})

// Our function to handle the response.
async function handleRequest(request) {
  // Only GET requests work with this proxy.
  if (request.method !== 'GET')
  return MethodNotAllowed(request);
  // The URL that is being requested.
  const url = new URL(request.url);
  // Request "origin URL" aka the real blog instead of what was requested.
  // This switches out the absolute URL leaving the relative path unchanged. 
  const originUrl = url.toString().replace('https://example.com', 'https://blog.example.com');
  // The contents of the origin page.
  const originPage = await fetch(originUrl);
  // Give the response our origin page.
  const newResponse = new Response(originPage.body, originPage); return newResponse;
}

// Hey! GET requests only 
function MethodNotAllowed(request) {
  return new Response(`Method ${request.method} not allowed.`, {
    status: 405,
    headers: { 'Allow': 'GET' }
  })
}

最后,单击 保存并部署

步骤 3:添加路由

现在让我们通知 Cloudflare 在哪些 URL(也称为 Routes)上运行这段代码。转到 Cloudflare 中的网站,然后单击 Workers

Cloudflare 的主屏幕上有一个 Workers 部分,您可以在其中编辑代码,然后每个网站都有一个 Workers 部分,您可以在其中添加路由。它们是两个不同的位置,而且很令人困惑。

首先,单击 添加路由

因为我们要添加一个包含许多子页面的博客,所以我们将使用 https://example.com/blog*。请注意,星号用作匹配通配符。这段代码将在博客页面和以 blog 开头的每个页面上运行。

这可能会有意想不到的后果。例如,您有一个以“blog”开头的页面,但不是实际博客的一部分,例如 https://example.com/blogging-services。该页面将与此规则匹配。

然后,在 服务下拉菜单中选择 Worker。

我们已经完成了大部分工作,但是我们需要添加更多路由——博客依赖的 CSS、JavaScript 和其他文件路径(除非所有文件都托管在不同的 URL 上,例如 CDN 上)。一个好的查找方法是测试您的路由并检查控制台。

转到您的 https://example.com/blog 并确保某些内容正在加载。它看起来很乱,因为它缺少主题文件。现在没关系,只要它没有产生 404 错误即可。重要的是打开浏览器的开发者工具,启动控制台,并记录所有找不到或无法加载的红色 URL(通常是 404 或 403),这些 URL 是您域的一部分。

橙色框中的资源是我们需要定位的资源。

您需要将它们添加为路由……但只添加父路径。所以,如果您的红色 URL 是 https://example.com/wp-content/themes/file1.css,那么将 https://example.com/wp-content* 作为您的路由。如果您想更具体,也可以添加子路径,但想法是使用一个路由来捕获大部分文件。

添加这些路由后,查看您的 URL 并查看它是否看起来像您的子域名。如果没有,请检查前面的步骤。(您很可能需要添加更多路由。)

最好通过导航到多个页面并查看是否缺少任何内容来进行质量检查。我还建议打开开发者工具并搜索您的子域名(blog.example.com)。如果显示出来,您要么需要添加路由来定位这些资源,要么对您的平台进行一些操作以停止输出这些 URL。例如,我的平台输出一个带有子域的规范链接标签,所以我找到一个插件来修改规范 URL 为我的根域。

步骤 4:最隐秘的秘诀(noindex

您可能会发现我们遇到了一个问题。我们的 URL 在两个不同的 URL 上可用。是的,我们可以使用 canonical 属性来通知 Google 哪个 URL 是我们的“主” URL,但不要将选择权留给 Google。

首先,将整个子域名设置为 noindex(具体方法因平台而异)。然后,在 Cloudflare Worker 中,我们将添加以下代码行,它基本上表示在通过代理访问当前 URL 时删除 noindex

newResponse.headers.delete("x-robots-tag");

完整的代码解决方案将在本文末尾提供。

步骤 5:修改网站地图

最后要做的就是修改子域名的网站地图,使其不使用子域名。具体方法因平台而异,但目标是修改网站地图中的基础/绝对/域名,使其打印 example.com/mypost)而不是 blog.exmaple.com/mypost。某些插件和模块允许在没有自定义代码的情况下执行此操作。

就是这样!该解决方案应该可以正常工作!

局限性

Cloudflare 的这个魔法并非没有缺点。例如,它只接受 `GET` 请求,这意味着我们只能从服务器获取数据。我们无法使用 `POST`,而这正是表单使用的方式。因此,如果您需要让访客登录或提交表单,除了我们已经完成的工作之外,还需要做更多工作。我在 另一篇文章 中讨论了几个解决方案。

如前所述,另一个局限性是,在 Shopify 上使用这种方法需要订阅 Cloudflare 的企业定价套餐。同样,这是因为 Shopify 也使用 Cloudflare,并限制了在其他计划上代理流量的能力。

如果您尝试将两个相同平台的实例合并在一起(例如,您的顶级域名和子域名都使用 WordPress),您也可能会遇到一些问题。但在这种情况下,您应该能够合并并使用一个平台实例。

完整解决方案

以下是代码的全部内容

// Listen for every request and respond with our function.
// Note, this will only run on the routes configured in Cloudflare.
addEventListener('fetch', function(event) {
  event.respondWith(handleRequest(event.request))
})
// Our function to handle the response.
async function handleRequest(request) {
  // Only GET requests work with this proxy.
  if (request.method !== 'GET') return MethodNotAllowed(request);
  // The URL that is being requested.
  const url = new URL(request.url);
  // Request "origin URL" aka the real blog instead of what was requested.
  // This switches out the absolute URL leaving the relative path unchanged. 
  const originUrl = url.toString().replace('https://example.com', 'https://blog.example.com');
  // The contents of the origin page.
  const originPage = await fetch(originUrl);
  // Give the response our origin page.
  const newResponse = new Response(originPage.body, originPage);
  // Remove "noindex" from the origin domain.
  newResponse.headers.delete("x-robots-tag");
  // Remove Cloudflare cache as it's meant for WordPress.
  // If you are using Cloudflare APO and your blog isn't WordPress, (but
  // your main domain is), then stop APO from running on your origin URL.
  // newResponse.headers.set("cf-edge-cache", "no-cache"); return newResponse; 
}
// Hey! GET requests only 
function MethodNotAllowed(request) {
  return new Response(`Method ${request.method} not allowed.`, {
    status: 405,
    headers:
    { 'Allow': 'GET' }
  })
}

如果您在使用过程中需要帮助,欢迎您通过我的网站 CreateToday.io 与我联系,或查看 我的 YouTube 频道 以获取视频演示。