Cloudflare for SaaS:为客户绑定自定义域名的完整实现

详解 Cloudflare for SaaS 的完整实现:为客户站点绑定自定义域名的技术架构、SSL 证书自动签发、CNAME 接入流程、回源路由配置、与主域名的隔离策略。含 Workers 路由分发与 SaaS 自定义域名最佳实践。

Cloudflare for SaaS(原 SSL for SaaS)是 Cloudflare Enterprise 提供的核心能力:让你的 SaaS 客户可以使用自己的域名(如 customer.com)访问你的平台,而域名解析和 SSL 证书管理全部自动完成。对于任何需要"白标品牌化"的多租户 SaaS 产品,这是几乎不可或缺的基建能力。本文详解从架构设计到完整实现的全部步骤。


一、Cloudflare for SaaS 的核心能力

1.1 什么是 Cloudflare for SaaS?

场景:你的 SaaS 平台运行在 my-saas-platform.com

客户 A 想要用自己的域名:app.customer-a.com
客户 B 想要用自己的域名:portal.customer-b.io

目标:
  app.customer-a.com → 你的 SaaS 平台
  portal.customer-b.io → 你的 SaaS 平台
  同时:
    - 自动签发 SSL 证书(不需要客户操作)
    - 客户只需添加一条 CNAME 记录
    - 你的平台能识别是哪个客户的请求

1.2 核心组件

组件功能
Custom Hostnames声明客户域名,触发 SSL 签发流程
Fallback Origin默认回源地址(你的源站)
SSL Certificates自动签发和续期(支持 Let’s Encrypt / Google Trust Services)
Hostname Validation验证客户域名所有权(HTTP / CNAME)
Custom Metadata附在每个请求上的元数据(如 tenant_id)

二、架构设计

2.1 流量路由架构

用户访问 https://app.customer-a.com
  ↓
Cloudflare Edge(Anycast 全球节点)
  ├── DNS 解析到 Cloudflare IP
  ├── SSL 终止(自动签发的证书)
  ├── WAF / DDoS 防护
  ├── Custom Metadata 注入
  └── 回源到 Fallback Origin
  ↓
你的源站(如 Vercel / Render / 自建)
  ├── 读取 Host 头:app.customer-a.com
  ├── 读取 Custom Metadata:tenant_id = "customer-a"
  └── 路由到对应租户内容

2.2 数据库设计

CREATE TABLE custom_hostnames (
  id BIGSERIAL PRIMARY KEY,
  tenant_id VARCHAR(255) NOT NULL,
  hostname VARCHAR(255) NOT NULL UNIQUE,
  status VARCHAR(50) DEFAULT 'pending', -- pending/active/error
  ssl_status VARCHAR(50) DEFAULT 'initializing',
  created_at TIMESTAMP DEFAULT NOW(),
  validated_at TIMESTAMP,
  ssl_issued_at TIMESTAMP
);

三、开通与配置

3.1 开通 Cloudflare for SaaS

Cloudflare Dashboard → 你的主域名
→ SSL/TLS → Custom Hostnames
→ 启用 "Cloudflare for SaaS"
→ 设置 Fallback Origin(如 origin.my-saas.com)

Fallback Origin 是客户域名未匹配时的默认回源地址。通常指向你的主应用。

3.2 API 创建 Custom Hostname

# 为客户创建自定义域名
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/custom_hostnames" \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "hostname": "app.customer-a.com",
    "ssl": {
      "method": "http",
      "type": "dv",
      "settings": {
        "min_tls_version": "1.2"
      }
    },
    "custom_metadata": {
      "tenant_id": "customer-a"
    }
  }'

3.3 客户侧操作

客户需要在他们的 DNS 服务商添加一条 CNAME:

类型:CNAME
主机:app
值:customers.my-saas-platform.com(你的 SaaS CNAME 目标)

不需要客户做任何 SSL 证书操作。Cloudflare 会自动为 app.customer-a.com 签发证书,通常 1-5 分钟内完成。

3.4 验证状态

# 检查自定义域名的 SSL 状态
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/custom_hostnames/$HOSTNAME_ID" \
  -H "Authorization: Bearer $API_TOKEN"

# 期望状态
{
  "result": {
    "hostname": "app.customer-a.com",
    "ssl": {
      "status": "active"
    }
  }
}

四、在 Workers 中读取 Custom Metadata

// workers/index.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // 获取客户的 hostname
    const hostname = request.headers.get('Host'); // app.customer-a.com

    // 获取 Cloudflare 注入的 Custom Metadata
    const tenantId = request.headers.get('CF-Custom-Tenant-Id') || 'default';

    // 根据租户 ID 路由到不同内容
    const response = await fetch(`https://origin.my-saas.com${new URL(request.url).pathname}`, {
      headers: {
        'X-Tenant-ID': tenantId,
        'X-Original-Host': hostname,
      },
    });

    return response;
  },
};

如果你的应用支持多域名直接接入(如在 Next.js 中读取 headers().get('host')),不需要额外的 Workers 层。


五、完整 SaaS 域名接入流程

5.1 客户自助绑域名流程

1. 客户在 SaaS 后台输入域名:"app.customer-a.com"
   → 后端调用 Cloudflare API 创建 Custom Hostname

2. 系统返回 DNS 配置指南:
   "请添加以下 DNS 记录:
    CNAME  app  customers.my-saas-platform.com"

3. 客户在其 DNS 服务商添加记录

4. 系统轮询验证(每 30 秒检查一次 Cloudflare API)

5. 验证通过 → SSL 证书自动签发
   → 网站状态变为 "Active"
   → 客户收到邮件通知

5.2 后端实现(Node.js)

// lib/cloudflare-saas.ts
const CF_API_BASE = 'https://api.cloudflare.com/client/v4';

export async function createCustomHostname(hostname: string, tenantId: string) {
  const res = await fetch(`${CF_API_BASE}/zones/${ZONE_ID}/custom_hostnames`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${CF_API_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      hostname,
      ssl: {
        method: 'http',
        type: 'dv',
      },
      custom_metadata: {
        tenant_id: tenantId,
      },
    }),
  });

  return await res.json();
}

export async function getCustomHostnameStatus(hostnameId: string) {
  const res = await fetch(`${CF_API_BASE}/zones/${ZONE_ID}/custom_hostnames/${hostnameId}`, {
    headers: { 'Authorization': `Bearer ${CF_API_TOKEN}` },
  });

  const data = await res.json();
  return {
    hostname: data.result.hostname,
    sslStatus: data.result.ssl?.status,
    validationStatus: data.result.status,
  };
}

export async function deleteCustomHostname(hostnameId: string) {
  await fetch(`${CF_API_BASE}/zones/${ZONE_ID}/custom_hostnames/${hostnameId}`, {
    method: 'DELETE',
    headers: { 'Authorization': `Bearer ${CF_API_TOKEN}` },
  });
}

六、计费与限制

维度说明
定价包含在 Enterprise 计划中($5000/月起),无额外 Custom Hostname 费用
数量限制Enterprise:默认 5000 个 hostname(可扩展)
SSL 证书免费自动签发(Let’s Encrypt / Google Trust Services)
Wildcard 证书支持 *.customer-a.com
回源次数同主域名的出网流量计费

七、常见问题(FAQ)

客户需要把域名 DNS 托管到 Cloudflare 吗?

不需要。客户只需在自己的 DNS 服务商(阿里云、GoDaddy、Route53 等)添加一条 CNAME 记录指向你的 SaaS 目标即可。SSL 证书由 Cloudflare 在用户域名上独立签发

如果客户已经有 Cloudflare 账号怎么办?

有两种模式:

  1. Partial (CNAME) Setup:非 Cloudflare 托管 → 直接 CNAME 到 SaaS
  2. Full Zone Setup:客户域名也在 Cloudflare → 使用 Cloudflare for SaaS 的 ownership_verification 流程

通常 Partial Setup(CNAME 方式)更简单。

SSL 证书是如何自动签发的?

Cloudflare 使用自动 ACME 协议(同 Let’s Encrypt):

  1. 验证客户域名所有权(通过 HTTP challenge 或 CNAME)
  2. 向 Let’s Encrypt / Google Trust 申请证书
  3. 在边缘节点部署证书
  4. 自动续期(每 90 天)

可以为同一个客户绑定多个域名吗?

可以。一个租户可以有多个 Custom Hostnames:

如何防止域名被恶意绑定?

  1. 验证域名所有权:要求客户在域名上放置验证文件或 TXT 记录
  2. Hostname 白名单:在系统中限制只允许特定域名后缀
  3. 手动审核:对新绑定的域名进行人工审核
  4. 速率限制:限制单个租户绑定域名的频率

相关阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「saas」更多文章