# 连接自定义域名

> 使用 Cloudflare 作为反向代理，将你自己的域名指向 Daki 上托管的 Web 应用 — 免费 HTTPS 和自定义端口转发。包含高级的 Cloudflare Tunnel（cloudflared）替代方案。

想让你的 Web 应用可以通过 `yourdomain.com` 访问，而不是 `hostname:port` 地址？本指南将向你展示如何使用 **Cloudflare** 作为免费反向代理，把你自己的域名指向 Daki 服务器。Cloudflare 会为你处理 HTTPS，并把访问者转发到你的应用所监听的自定义端口。

> **信息:** 自定义域名需要**付费套餐**。它依赖可公开访问的端口，只有付费套餐（Bronze、Silver、Gold 以及游戏服务器套餐）才包含公网端口。Free Tier 服务器无法接收入站连接，因此自定义域名在那里无法使用。参见[付费套餐](https://daki.cc/zh/wiki/guides/paid-plans)和[网络与端口分配](https://daki.cc/zh/wiki/guides/network-allocations)。

## 你将学到

- 为什么 Daki 上的自定义域名需要 Cloudflare 这样的反向代理
- 如何通过已代理的 DNS 记录将域名指向你的服务器
- 如何为你的域名启用免费 HTTPS
- 如何使用 Origin Rule 将流量转发到应用的端口
- 使用 Cloudflare Tunnel（`cloudflared`）的高级替代方案

---

## 前提条件

- 一台带有公网端口分配的**付费套餐**服务器 — 参见[付费套餐](https://daki.cc/zh/wiki/guides/paid-plans)
- 一个你拥有的**域名**（在任意注册商注册，例如 Namecheap、GoDaddy、IONOS、Cloudflare Registrar）
- 一个免费的 [Cloudflare](https://dash.cloudflare.com/sign-up) 账户
- 一个正在运行、并监听服务器分配端口的 **Web 应用**（HTTP/HTTPS）

> **警告:** 你的应用必须在分配端口上绑定到 `0.0.0.0`（所有接口），**而不是** `127.0.0.1` / `localhost`。只绑定到 localhost 的应用无法从容器外部访问，代理将无法连接。大多数框架都接受 host 参数（例如 Flask 中的 `app.run(host="0.0.0.0", port=...)`，或许多 Node.js 应用中的 `HOST=0.0.0.0`）。

---

## 为什么需要 Cloudflare

Daki 的各个节点共享 **IP 地址**，而你的应用监听的是非标准**端口**（分配给你服务器的端口）。由此会产生两个问题：

- 访问者希望通过标准 HTTPS 端口（443）访问 `https://yourdomain.com` — 而不是 `yourdomain.com:4129`。
- 你希望获得有效的 HTTPS 证书，而无需自己管理。

在你的服务器前面放置 Cloudflare，可以同时解决这两个问题。Cloudflare 充当**反向代理**：浏览器通过 443 端口的 HTTPS 连接 Cloudflare，再由 Cloudflare 把请求转发到你服务器的自定义端口。你将获得免费 SSL 和简洁的 URL，同时服务器的真实 IP 保持隐藏。

> **注意:** 此方法适用于 **HTTP/HTTPS Web 应用**（网站、API、仪表板、Webhook）。它**不适用于**大多数游戏服务器等原始 TCP/UDP 服务 — Cloudflare 的标准代理只处理 HTTP(S)。对于非 HTTP 服务，请使用下方的 [Cloudflare Tunnel](#advanced-alternative-cloudflare-tunnel-cloudflared) 方法，或联系[支持](https://daki.cc/zh/wiki/guides/support)。

---

## 查找服务器的 IP 和端口

你需要从 [门户](https://daki.cc/zh/wiki/guides/portal) 的 **Network** 标签页中获取两个值：

| 值 | 获取位置 | 示例 |
| :--- | :--- | :--- |
| **服务器 IP** | 服务器主机名解析到的 IP 地址（显示在 **Network** 标签页中）。如果只显示主机名，你可以自行解析 — 例如 `ping eu-node1.daki.cc` — 或询问[支持](https://daki.cc/zh/wiki/guides/support)。 | `203.0.113.10` |
| **分配端口** | **Network** 标签页中主机名旁显示的端口。你的应用必须监听此端口。 | `4129` |

本指南全程使用这些示例值。出现的地方都请**替换为你自己的值**。关于分配的更多信息，参见[网络与端口分配](https://daki.cc/zh/wiki/guides/network-allocations)。

---

## 第 1 部分：Cloudflare 设置

**步骤 1: 将你的域名添加到 Cloudflare**

1. 在 [dash.cloudflare.com](https://dash.cloudflare.com/sign-up) 创建一个免费账户（或登录）。
2. 点击 **Add a site**，输入你的域名（例如 `yourdomain.com`）。
3. 出现提示时，选择 **Free** 套餐。
4. Cloudflare 会提供**两个 nameserver**（例如 `xxx.ns.cloudflare.com`）。
5. 登录你的**域名注册商**，找到该域名的 nameserver / DNS 设置，并将现有的 nameserver 替换为 Cloudflare 提供的两个。

> **注意:** nameserver 的更改可能需要几分钟到 24 小时才能生效。域名激活后，Cloudflare 会发邮件通知你。在此期间，你可以继续后面的步骤。

**步骤 2: 创建 DNS 记录**

在你的域名的 Cloudflare 控制台中，进入 **DNS → Records**，然后点击 **Add record**：

| 字段 | 值 |
| :--- | :--- |
| **Type** | `A` |
| **Name** | `@`（表示根域名 `yourdomain.com`） |
| **IPv4 address** | 你的服务器 IP（示例：`203.0.113.10`） |
| **Proxy status** | **Proxied**（橙色云朵 ☁️） |

点击 **Save**。

> **提示:** **橙色云朵（Proxied）**状态至关重要 — 它让流量经过 Cloudflare，使 SSL 和第 2 部分中的端口重写得以生效。灰色云朵（DNS only）会完全绕过 Cloudflare，并暴露你服务器的 IP。若还要提供 `www.yourdomain.com`，请再添加一条名为 `www` 的已代理记录。

**步骤 3: 配置 SSL/TLS**

1. 进入 **SSL/TLS → Overview**。
2. 将加密模式设置为 **Flexible**。

**Flexible** 表示访问者与 Cloudflare 之间的流量是加密的（HTTPS），而 Cloudflare 通过明文 HTTP 连接你自定义端口上的应用。当你的应用提供明文 HTTP 时（大多数运行在自定义端口上的应用都是如此），这是正确的选择。

> **注意:** 如果你的应用自行终止 TLS（即它在自己的端口上直接提供 **HTTPS**），请改选 **Full**。注意，**Flexible** 不会加密 Cloudflare 与你服务器之间的连接；对于敏感流量，请优先使用 **Full** 并在应用中启用 HTTPS。

---

## 第 2 部分：将流量转发到应用端口

由于你的应用运行在自定义端口（例如 `4129`）而不是标准的 80/443，你需要告诉 Cloudflare 在连接你的服务器时使用哪个端口。**Origin Rule** 正好能做到这一点 — 它会重写传入请求的目标端口。

**步骤 1: 创建 Origin Rule**

1. 在 Cloudflare 控制台中，进入 **Rules → Origin Rules**。
2. 点击 **Create Origin Rule**。
3. 配置规则：

| 设置 | 值 |
| :--- | :--- |
| **When incoming requests match** | Hostname **equals** `yourdomain.com` |
| **Then... Rewrite to** | **Destination Port** → `4129`（你的分配端口） |

4. 点击 **Save**（或 **Deploy**）。

> **完成:** DNS 生效后，你的应用即可通过 **`https://yourdomain.com`** 访问 — Cloudflare 通过 HTTPS 提供服务，并把流量转发到你服务器的 `4129` 端口。

---

## 验证是否生效

1. 确保你的服务器**正在运行**，并且应用在分配端口上监听（在 **Console** 标签页中检查）。
2. 在浏览器中打开 `https://yourdomain.com`。
3. 你应该能看到应用通过 HTTPS 正常加载，并显示有效的锁形图标。

如果不能立即加载，请稍等片刻让 DNS 生效，然后参见下方的[常见问题](#common-issues)。

---

## 高级替代方案：Cloudflare Tunnel（cloudflared）

除了开放公网端口并将 A 记录指向你的服务器，你还可以在应用旁运行 **`cloudflared`** — Cloudflare 的隧道客户端。它会向 Cloudflare 建立一个**出站**连接，并在本地把流量转发到你的应用，因此你服务器的 IP 和端口保持隐藏，既不需要 DNS A 记录，也不需要 Origin Rule。

> **注意:** **Daki 环境如何影响此方法。** Startup 标签页提供两个命令 — [**安装命令**](https://daki.cc/zh/wiki/guides/startup-settings)（在首次设置或重新安装时运行一次）和**启动命令**（每次启动时运行）。你也可以临时把启动命令设为 `bash` 来打开一个 Shell。但你的服务器是**容器，而不是完整的 Linux 虚拟机**：没有 `apt`，你无法注册系统服务，而你在控制台中手动启动的隧道会在服务器重启时终止。因此，可靠的做法是把隧道作为**应用自身启动流程的一部分** — 在运行时下载 `cloudflared` 二进制文件，并通过环境变量控制，将其作为子进程与应用一同启动。

**步骤 1: 创建隧道**

1. 在 Cloudflare **Zero Trust** 控制台中，进入 **Networks → Tunnels**，点击 **Create a tunnel**，然后选择 **Cloudflared**。
2. 为隧道命名，并**复制 Cloudflare 显示的隧道令牌**（即安装命令中 `--token` 后面的长字符串）。
3. 为隧道添加一个 **Public Hostname**：
   - **Domain：** `yourdomain.com`
   - **Service：** `http://localhost:4129`（你应用的本地端口）

**步骤 2: 将令牌保存到 .env**

将令牌添加到服务器的 `.env` 文件中（参见[环境变量](https://daki.cc/zh/wiki/guides/environment-variables)），这样它就不会出现在你的源代码里：

```

CLOUDFLARE_TUNNEL_TOKEN=your_tunnel_token_here

```

**步骤 3: 从代码中启动隧道**

添加一个小型启动脚本：它会在首次运行时下载 `cloudflared`，并在你的应用旁边启动它，然后把该脚本设为你的**启动命令**。只有设置了 `CLOUDFLARE_TUNNEL_TOKEN` 时才会启动隧道，因此同一份代码在本地没有令牌时也能正常运行。

**Node.js（start.js）**

保存为 `start.js`，然后将启动命令设为 `node start.js`：

```js

// start.js — starts your app and, if a token is set, a Cloudflare tunnel.

require("dotenv").config();



const fs = require("fs");

const path = require("path");

const { spawn, spawnSync } = require("child_process");



const CLOUDFLARED_URL =

  "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64";

const binaryPath = path.join(__dirname, "cloudflared");

const children = [];



// The container has no apt — download the binary on first run, reuse it after.

function ensureCloudflared() {

  if (fs.existsSync(binaryPath)) return binaryPath;

  console.log("[tunnel] downloading cloudflared...");

  const dl = spawnSync("curl", ["-fsSL", "-o", binaryPath, CLOUDFLARED_URL], {

    stdio: "inherit",

  });

  if (dl.status !== 0) return null;

  fs.chmodSync(binaryPath, 0o755);

  return binaryPath;

}



// Spawn a child process; if any child exits, stop the rest and quit.

function start(name, command, args) {

  const child = spawn(command, args, { stdio: "inherit", env: process.env });

  child.on("exit", (code) => {

    console.error(`[${name}] exited (${code}); shutting down`);

    for (const c of children) if (!c.killed) c.kill("SIGTERM");

    process.exit(code ?? 1);

  });

  children.push(child);

}



const token = process.env.CLOUDFLARE_TUNNEL_TOKEN?.trim();

if (token) {

  const binary = ensureCloudflared();

  if (binary) start("tunnel", binary, ["tunnel", "run", "--token", token]);

} else {

  console.log("[tunnel] CLOUDFLARE_TUNNEL_TOKEN not set; skipping tunnel");

}



// Start your app — change this to your real entry point.

start("app", "node", ["index.js"]);

```

**Python / 其他（start.sh）**

保存为 `start.sh`，然后将启动命令设为 `bash start.sh`：

```bash

#!/usr/bin/env bash

# Load variables from .env into the environment.

set -a; [ -f .env ] && . ./.env; set +a



# The container has no apt — download cloudflared on first run, reuse it after.

if [ ! -x ./cloudflared ]; then

  curl -fsSL -o ./cloudflared \

    https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64

  chmod +x ./cloudflared

fi



# Start the tunnel in the background only if a token is set.

if [ -n "$CLOUDFLARE_TUNNEL_TOKEN" ]; then

  ./cloudflared tunnel run --token "$CLOUDFLARE_TUNNEL_TOKEN" &

fi



# Start your app in the foreground — change this to your real command.

exec python main.py

```

> **提示:** 为避免每次启动都重新下载，你可以把下载步骤移到[安装命令](https://daki.cc/zh/wiki/guides/startup-settings)中，只在启动命令里保留启动逻辑。上面的示例每次启动时运行也是安全的 — 二进制文件存在后会跳过下载。

**取舍：**

| | Cloudflare 代理（第 1–2 部分） | Cloudflare Tunnel（cloudflared） |
| :--- | :--- | :--- |
| 设置难度 | 较简单（仅需控制台） | 较高级（需整合进应用的启动流程） |
| 暴露服务器 IP/端口 | 隐藏在代理之后 | 完全隐藏（仅出站） |
| 需要公网端口 / Origin Rule | 需要 | 不需要 |
| 占用服务器资源 | 服务器上无额外占用 | 运行一个额外进程 |

---

## 常见问题

**网站完全无法加载**

DNS 可能仍在生效中。nameserver 的更改最长可能需要 24 小时（通常远少于此）。请在 Cloudflare 中确认你的域名状态为 **Active**，并且 **A 记录** 已存在且为 **Proxied**（橙色云朵）。你可以使用 [dnschecker.org](https://dnschecker.org) 之类的工具检查生效情况。

**错误 522 / 523 — 连接超时 / 源站不可达**

Cloudflare 无法连接到你的应用。请按以下顺序检查：
- 你的**服务器正在运行**，并且应用已完全启动（在 **Console** 标签页中检查）。
- **Origin Rule** 把端口重写为你准确的**分配端口**。
- **A 记录的 IP** 与你服务器的 IP 一致。
- 你的应用绑定到 **`0.0.0.0`**，而不是 `127.0.0.1`/`localhost`。

**重定向次数过多 / ERR_TOO_MANY_REDIRECTS**

这通常是 SSL 模式不匹配导致的。如果你的应用强制使用 HTTPS，而 Cloudflare 通过 HTTP 连接它，就会形成重定向循环。你可以让应用提供明文 HTTP（并使用 **Flexible**），或让应用提供 HTTPS，并在 **SSL/TLS → Overview** 中把 Cloudflare 切换为 **Full**。

**http:// 可以访问，但 https:// 不行**

确保你的 DNS 记录为 **Proxied**（橙色云朵），并且已设置 **SSL/TLS** 模式（Flexible 或 Full）。如果是灰色云朵（DNS only），Cloudflare 的 SSL 会被绕过。

**我的游戏服务器 / 非 HTTP 服务无法工作**

Cloudflare 的标准代理只处理 HTTP/HTTPS。对于大多数游戏服务器这类原始 TCP/UDP 服务，此方法不适用。请直接使用主机名和端口连接你的服务器（参见[网络与端口分配](https://daki.cc/zh/wiki/guides/network-allocations)），或就其他方案联系[支持](https://daki.cc/zh/wiki/guides/support)。

**必须使用 Cloudflare 吗？**

不必。Cloudflare 是最简单的免费选项，但任何能转发到自定义端口的反向代理都可以使用（例如通过 `cloudflared` 的隧道，或在应用前自行部署的 Nginx/Caddy）。推荐使用 Cloudflare，因为它提供免费 SSL、隐藏你的服务器 IP，并且在代理方式下无需在服务器上安装额外软件。

---

## 下一步

- [网络与端口分配](https://daki.cc/zh/wiki/guides/network-allocations) — 查找你的主机名和端口，并添加更多分配。
- [付费套餐](https://daki.cc/zh/wiki/guides/paid-plans) — 升级到付费套餐以获得公网端口。
- [环境变量](https://daki.cc/zh/wiki/guides/environment-variables) — 在代码中从环境变量读取端口。
- [获取帮助](https://daki.cc/zh/wiki/guides/support) — 遇到困难时联系支持。

---
Source: https://daki.cc/zh/wiki/guides/custom-domain
