我有个自己用的网页导航页,收藏的都是平时常用的网站和各种管理面板入口,手机、电脑打开就能点。之前它跑在一台 VPS 上,用的是 Flare 这个开源导航程序,套着 Cloudflare 隧道对外访问。

前阵子这台 VPS 的线路出了点问题,隧道时不时断一下,导航页跟着打不开。导航这东西说白了就是一扇门,门三天两头打不开,比没有还闹心。于是我就想:能不能让导航页不依赖任何一台服务器?

选型:只在 Cloudflare 上跑的导航

我的要求不多:

  • 不用服务器,最好直接跑在 Cloudflare 上

  • 有分类,后台能管理

  • 书签能设为私密,没登录的人看不到

  • 能导入导出,方便以后再搬家

在 GitHub 上翻了一圈,候选有好几个。有的项目已经归档不维护了,有的把数据存在 GitHub 仓库里(总觉得不踏实),有的绕了一圈还是得要一台服务器。最后选了 CF-Navs,理由很实际:更新勤快,有两级分类和完整的后台管理,跟我原来用 Flare 的习惯最接近;数据存在 Cloudflare 的 D1 数据库里;还支持 JSON 和浏览器书签的导入导出。

部署:全程网页操作

部署比我想的简单,全程点鼠标,不用敲命令:

  1. Fork 项目:在 GitHub 上把 CF-Navs 仓库 Fork 一份到自己的账号。以后升级就靠同步这个 Fork,所以这一步不能省。

  2. 关联仓库部署:打开 Cloudflare 控制台,进「Workers 和 Pages」,选择从 GitHub 导入刚才 Fork 的仓库,构建设置保持项目默认,点部署。等一两分钟 Worker 就跑起来了,这时会有一个 workers.dev 的临时地址。

  3. 建数据库和 KV:在控制台里新建一个 D1 数据库(存书签和分类)和一个 KV 命名空间(存登录会话),名字随便起,自己能认出来就行。

  4. 绑定到 Worker:回到这个 Worker 的「设置 → 绑定」,把刚建的 D1 和 KV 都绑上去。绑定时填的变量名要按项目说明里写的来,填错了导航会打不开、或者数据存不进去——我当时对着说明核了两遍才敢保存。

  5. 初始化:打开临时地址会进入初始化页面。按提示先给 Worker 设一个一次性的安装令牌(环境变量),用它完成初始化,再设好管理员的用户名和密码。

  6. 绑自己的域名:在 Worker 的「设置 → 域和路由」里添加自定义域,填导航的域名,Cloudflare 会自动把解析配好,不用自己去加记录。

  7. 关门收尾:确认自定义域名能正常打开后做两件事——把第 5 步的安装令牌从环境变量里删掉;在设置里关掉 workers.dev 入口(预览入口也一起关)。这样导航就只剩自己的域名一个进口,临时地址再打开就是 404。

  8. 以后升级:上游出了新版时,在 GitHub 上对 Fork 点一次「Sync fork」同步,Cloudflare 会自动重新部署。升级前记得先在导航后台导出一份 JSON 备份,真出问题还能导回来。

搬书签:格式对不上,手工转一手

原来的 Flare 书签导出是个 YAML 文件,CF-Navs 不认。好在它有自己的 JSON 备份格式,我写了个小脚本,把 YAML 按它的格式转成 JSON 再导入。转换后的文件大概长这样(链接已换成示例):

{
  "version": 2,
  "categories": [
    { "id": 1, "name": "常用工具", "sort": 0 }
  ],
  "bookmarks": [
    {
      "name": "示例站点",
      "url": "https://example.com",
      "category_id": 1,
      "is_private": 1
    }
  ]
}

一共 10 个分类、67 条书签,全部设成私密。导完之后我特意退出登录验了一遍:未登录状态下接口返回是空的,外人一条都看不到,这才放心。

中间还有个小插曲:Flare 首页置顶的那几个面板入口,不在导出的书签文件里。找了一圈也没弄清它们存在哪,干脆单独做了一个补充文件,把 5 个面板入口补进去。这里有个坑要提醒:导入的时候一定要选"合并",千万别选"覆盖",选覆盖会把刚导进去的 67 条全清掉。

大门留不留:我选择留着

导航前面还挂着一层 Cloudflare Access(邮箱验证码登录,会话时长一个月,平时基本感觉不到)。它的设置都在 Cloudflare 的 Zero Trust 后台里,流程不复杂:

  1. 进入 Zero Trust,打开「访问控制」→「应用程序」,添加一个自托管(Self-hosted)应用

  2. 应用名随便起,域名填导航用的那个域名

  3. 会话时长我设成了 1 个月,这样不用频繁验证

  4. 登录方式选邮箱一次性验证码(One-time PIN),验证码发到邮箱里输进去就行(我还同时开了 Cloudflare 账号登录,两个方式并存)

  5. 给应用加一条访问策略:只放行我自己的邮箱,其他人一律挡在外面

这样设置完,任何人打开导航域名,都得先过邮箱验证码这一关,过不了的人连导航自己的登录页都看不到。

搬完之后我纠结过要不要把它拆掉,少一层少点麻烦。后来还是决定留着:Access 是大门,导航自己的账号密码是房门锁。拆了大门,登录页就直接暴露在公网上,谁都可以来试密码,乱七八糟的扫描流量还会白白消耗 Worker 的免费额度。一个月验一次的麻烦,换这份清净,值。

管理员密码生成之后直接存进我自己的密码管理器,本地随手记的临时副本确认删掉了。顺便提一句这个习惯:密码只进密码库,别散落在各种备忘录和聊天记录里。

现在的状态

搬完之后,导航页彻底跟那台线路不稳的 VPS 解绑,它再怎么抖也不影响我开门。费用方面,Cloudflare 的免费额度对个人自用来说绰绰有余,我这点访问量零头都用不到;而且免费版超额只会报错,不会偷偷扣费,付费得自己主动订阅才行,这一点我也专门查过账单,心里有数了。

折腾这一圈最大的感受是:导航、密码库这类"所有服务的入口",值得优先挑出来,放到不依赖单台机器的位置上。入口稳了,其他东西慢慢折腾都不慌。