抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前言

前面我已经把 Nexterm 部署到了 Hugging Face Spaces。但 HF 免费版有一个明显限制:不能直接绑定自定义域名。

最开始的需求很简单,只是想把:

ssh.007666.xyz

反代到:

jiaknama-nexterm.hf.space

这个用一个 Cloudflare Worker 就能解决。但很快就出现了新的问题:如果我以后还有很多 Space,每个 Space 都创建一个 Worker,会很麻烦。

所以最后做成了一个通用管理器:

任意子域名 *.007666.xyz

同一个 Cloudflare Worker

KV 里查映射关系

转发到对应的 *.hf.space

这样以后新增 Space,只需要在管理页面里加一条映射,不需要再写 Worker。

海灵操作分流闸门让一个 Worker 转发多个 Space

目标

这个工具要解决几个问题:

  1. 一个 Worker 管理多个 HF Space 反代。
  2. 支持 *.007666.xyz 这种通配子域名。
  3. 提供 Web 面板,能增删改查映射。
  4. 提供 API,方便脚本调用。
  5. 提供 OpenAPI,让 AI 工具也能调用。
  6. 保留 WebSocket 支持,避免 Nexterm 终端连接出问题。

最后的管理入口是:

https://spaces.007666.xyz

反代入口示例是:

https://ssh.007666.xyz -> https://jiaknama-nexterm.hf.space

整体结构

整个方案只用了 Cloudflare 的几个基础能力:

  • Cloudflare DNS:配置通配 CNAME。
  • Cloudflare Worker:处理请求转发。
  • Cloudflare KV:保存域名映射。
  • Worker Routes:把子域名流量交给 Worker。

结构大概是这样:

浏览器访问 app.007666.xyz


Cloudflare DNS / Proxy


Worker: hf-space-proxy-manager

├─ 如果是 spaces.007666.xyz:返回管理面板

└─ 如果是其他子域名:查 KV 映射并反代


对应的 Hugging Face Space

KV 里保存的映射类似这样:

{
"host": "ssh.007666.xyz",
"upstream": "jiaknama-nexterm.hf.space",
"note": "Nexterm",
"createdAt": "2026-07-05T00:00:00.000Z",
"updatedAt": "2026-07-05T00:00:00.000Z"
}

从单用途 Worker 到通用网关

一开始 Worker 里是写死上游域名的:

const UPSTREAM_HOST = "jiaknama-nexterm.hf.space";
const PUBLIC_HOST = "ssh.007666.xyz";

这适合单个服务,但不适合多个 Space。通用化以后,Worker 不再写死上游,而是根据当前访问的 Host 去 KV 查询。

核心思路是:

const host = url.hostname.toLowerCase();
const mapping = await env.MAPPINGS.get("host:" + host, "json");

if (!mapping) {
return new Response("未配置映射", { status: 404 });
}

url.hostname = mapping.upstream;

这样 ssh.007666.xyzdemo.007666.xyzapi.007666.xyz 都可以走同一个 Worker。

海灵从 KV 抽屉里取出域名到 Space 的映射卡片

反代逻辑

普通 HTTP 请求的处理大概分为几步。

1. 改写上游地址

const upstreamUrl = new URL(request.url);
upstreamUrl.protocol = "https:";
upstreamUrl.hostname = mapping.upstream;

路径和查询参数不变,只替换域名。

2. 改写请求头

headers.set("Host", mapping.upstream);
headers.set("Origin", `https://${mapping.upstream}`);
headers.set("Referer", `https://${mapping.upstream}${incomingUrl.pathname}${incomingUrl.search}`);
headers.set("X-Forwarded-Host", host);
headers.set("X-Forwarded-Proto", "https");

这样上游 HF Space 收到请求时,更像是用户直接访问 *.hf.space

3. 处理重定向

有些应用会返回 Location: https://xxx.hf.space/...。如果不处理,浏览器会跳回 HF 原始域名。

所以响应里要把 Location 改回自定义域名:

const location = responseHeaders.get("Location");
if (location) {
responseHeaders.set(
"Location",
location.replace(`https://${mapping.upstream}`, `https://${host}`)
);
}

4. 保留 WebSocket

Nexterm 这类终端工具会用 WebSocket。如果 Worker 把 WebSocket 当普通响应处理,终端连接可能会异常。

所以遇到升级请求时,直接返回上游响应:

if ((request.headers.get("Upgrade") || "").toLowerCase() === "websocket") {
return response;
}

管理面板

管理面板放在:

https://spaces.007666.xyz

它支持这些操作:

  • 新增映射。
  • 编辑映射。
  • 查看单条映射 JSON。
  • 删除映射。
  • 测试上游 Space 是否可访问。
  • 复制 cURL 调用示例。

登录方式很简单:管理员口令保存在 KV 的 config:adminToken 里。页面登录后会写一个 HttpOnly Cookie,同时前端也可以用同一个口令作为 Bearer Token 调 API。

这里没有做复杂账号系统,因为使用场景比较轻:这是一个个人反代管理面板,不是多用户平台。

API 设计

为了让脚本和 AI 都能调用,我把面板后端整理成标准 REST API。

认证方式:

Authorization: Bearer <ADMIN_TOKEN>

也支持:

X-API-Key: <ADMIN_TOKEN>

接口列表:

GET    /api/mappings
POST /api/mappings
GET /api/mappings/{host}
PUT /api/mappings/{host}
PATCH /api/mappings/{host}
DELETE /api/mappings/{host}
GET /api/mappings/{host}/probe

新增映射示例:

curl -X POST "https://spaces.007666.xyz/api/mappings" \
-H "Authorization: Bearer <ADMIN_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"host": "demo.007666.xyz",
"upstream": "your-space.hf.space",
"note": "测试 Space"
}'

更新映射示例:

curl -X PATCH "https://spaces.007666.xyz/api/mappings/demo.007666.xyz" \
-H "Authorization: Bearer <ADMIN_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"note": "更新备注"
}'

测试上游:

curl "https://spaces.007666.xyz/api/mappings/demo.007666.xyz/probe" \
-H "Authorization: Bearer <ADMIN_TOKEN>"

支持 AI 调用

为了让 AI Agent 或支持 Actions 的工具能直接调用,我加了两个入口。

OpenAPI 地址:

https://spaces.007666.xyz/openapi.json

AI Plugin Manifest:

https://spaces.007666.xyz/.well-known/ai-plugin.json

还有一个给人看的动作说明:

https://spaces.007666.xyz/api/ai/actions

这样 AI 工具只要拿到 OpenAPI 和管理员口令,就可以执行类似:

  • 查询当前有哪些映射。
  • 新增一个 Space 绑定。
  • 修改某个绑定。
  • 删除不需要的绑定。
  • 测试某个上游是否可访问。

海灵把 OpenAPI 卷轴交给脚本和 AI 调用

这里要注意:AI 能调用不代表应该完全放权。管理员口令权限很高,最好只放在可信 Agent 的密钥管理里,不要写进提示词。

部署方式

这次没有使用本地 Wrangler 登录部署,而是直接走 Cloudflare REST API。原因是手头已有 Cloudflare API 凭据,用 API 部署更直接。

部署脚本做了这些事:

  1. 查找 007666.xyz 对应的 Zone。
  2. 创建或复用 KV namespace。
  3. 上传 Worker 模块。
  4. 写入默认映射。
  5. 创建通配 DNS。
  6. 配置 Worker Routes。
  7. 保留已有管理员口令,避免升级时把自己锁在门外。

通配 DNS 类似:

*.007666.xyz CNAME jiaknama-nexterm.hf.space

Worker Route 类似:

*.007666.xyz/*
spaces.007666.xyz/*
ssh.007666.xyz/*

这里的 CNAME 目标其实只是为了让 DNS 记录成立,真正转发目标由 Worker 从 KV 中决定。

安全注意事项

这类工具很方便,但也有几个需要注意的点。

1. 不要长期使用 Global API Key

Cloudflare Global API Key 权限太大。临时用来部署可以,但长期最好换成 scoped API Token。

推荐最小权限大概是:

  • Zone DNS 编辑。
  • Workers Scripts 编辑。
  • Workers Routes 编辑。
  • KV Storage 编辑。

2. 管理口令不要写进代码

管理员口令应该放在 KV 或 Secret 里,不要写进 Worker 源码。

部署脚本也要避免每次重新生成口令,否则重新部署一次就可能登录不上。

3. Public Space 仍然是公开服务

Cloudflare 反代只是换了访问域名,不等于给 HF Space 自动加权限。

如果 Space 本身是公开的,那别人知道反代域名后也能访问。真正敏感的应用应该自己做登录鉴权。

4. AI 调用要限制 Token 暴露

OpenAPI 是公开描述,没问题。真正有权限的是 Bearer Token。

不要把管理员口令贴在公开对话、公开仓库或前端代码里。

总结

这个通用反代管理器解决的是一个很具体的问题:我有很多 Hugging Face Space,但不想每个 Space 都单独创建 Worker

最后的方案是:

通配 DNS + 单个 Worker + KV 映射 + 管理面板 + REST API + OpenAPI

现在新增一个 Space 绑定,只需要做一件事:打开 spaces.007666.xyz,添加一条 绑定域名 -> hf.space 域名 的映射。

后续如果继续增强,我可能会考虑:

  1. 支持自定义根域名,而不只限于 *.007666.xyz
  2. 给每条映射加启用/禁用开关。
  3. 增加访问日志或最近探测状态。
  4. 给 AI 调用单独生成低权限 Token,而不是复用管理员口令。

评论




站点访问量 Loading… 站点访客数 Loading…