自托管是笔经济账,不是道德账
· 阅读约 7 分钟
cal.com 四月份把核心产品闭源那天,圈子里又是一轮"开源自托管赢了"的欢呼。社区分叉 Cal.diy 当天就出来了,MIT 许可证,前 cal.com 实习生维护,README 里写着"研究用途,不鼓励生产使用"——这句话基本没人读,大家只看见了"MIT"和"开源"两个词,就急着宣布又一个商业闭源被打败了。
我先把话说清楚:我自己刚把网站上的 Calendly 预约组件换成了自托管的 Easy!Appointments。但这篇文章不是来给自托管唱赞歌的。恰恰相反,拆完这笔账之后我觉得,外面那套"重新夺回数据控制权"的爽文叙事,省略了这整件事里最贵的那部分成本。
先把对面那本账算明白,不然自托管的成本根本算不出基准线。
Calendly 的免费层在预约 SaaS 里是有名的"诱饵":个人用户永远免费,靠的是把企业客户当成真正的收入来源。它的定价结构大致是——免费层给你一条公开预约链接,但团队协作、工作流自动化、CRM 集成、品牌定制,全部要升级到付费层(来源:Calendly 公开定价页,2026 年 8 月存档)。这个定价逻辑的毛利来源很清楚:免费层用户是获客成本和网络效应的养料,付费层才是收钱的地方。SaaS 里 NRR 超过 120% 的公司,靠的都是这种"免费引流、付费收割"的结构。这套账,做 SaaS 的人都熟。
但这是他们的账。对用户来说,把业务流程——尤其是客户数据——放在一个你不拥有、不能审计、一旦对方调整定价或改条款你就只能跟着动的系统里,这部分成本从来没被 Calendly 的定价页算进去过。这不是什么新发现,每个经历过 SaaS 涨价或功能下架的开发者心里都有一笔"信任赤字",只是平时不说。
于是就有了我这边的选择:把服务器上跑了三年的 Calendly 组件换掉。
这个决策在外面被讲成"重新夺回数据控制权",听着很燃,但拆开算,这根本不是一个关于自由的故事,它是一个关于成本转移的故事。自托管不省钱,它只是把显性的订阅费换成了隐性的技能债——而且债主是你自己。
先把最容易算的那部分算了:服务器。我的场景是个人网站,Cloudflare Pages/Workers/D1 那一档基本零成本,这是真的。但这不是自托管系统的典型成本,这是一个特定技术栈(Svelte + TypeScript,部署在 Cloudflare 边缘网络上)的特例。真按自托管的"标准配置"来——一台 VPS、PHP + MySQL、再加上安全补丁和备份——月成本轻松到两位数美元。再加上每周的安全公告追踪、升级时可能被覆盖的本地补丁、以及"万一崩了谁负责"的运维工时,把人力按小时折算进去,自托管方案的 TCO 大概率比订阅费贵——而且没有客服。
这一步我是在审计两个候选项目时才算明白的。
我最初淘汰 CloudMeet 和 Cal.diy,理由非常不"开源原教旨":不是因为代码有问题,而是因为项目太年轻、没有声誉。CloudMeet 只有一个维护者,提交历史短得让人不安;Cal.diy 的文档自己都写着不鼓励生产使用。讽刺的是——我因为"声誉不够"淘汰了它们,然后我接下来的整个论证方向全都是"声誉不可靠"。这个矛盾我拖了两天才绕过去:声誉确实不可靠,但它仍然是成本最低的初筛手段,完全不看声誉直接读代码,是不现实的;可只停在声誉不看代码,就是懒惰。
于是我对剩下的两个做了真正的代码审计。Easy!Appointments,十年历史、GitHub 三千多星、PHP + MySQL、预约界面最接近 Calendly——这些名声都是真的,然后呢?读代码,四十分钟内就发现公共预约流程里有 TOCTOU 竞态条件:两个访客可以同时预订同一个时间段,因为可用性检查和写入之间隔着好几个操作,中间没有事务、没有锁,连数据库约束都没加。代码库里明明已经有一个正确实现了冲突检查的方法 has_provider_conflict(),但公共流程里根本没调用它。这事挺典型的:有人写过对的实现,但没接在业务路径上——比没有更害人,因为检查日志上显示"有这个方法",而实际上它在生产流程里是尸体。
更妙的是 booking-calendar 那边:SQLite 的串行写入机制把这个竞态完全掩盖了。因为它把所有写入操作排队串行执行,看起来一切正常。只要你把数据库换成 PostgreSQL 或 MySQL,同样的代码立刻暴露。这不是 bug,这是迁移陷阱——用 SQLite 做默认数据库,等于在开发环境里帮你把并发问题全部藏起来,等你上生产才发现。
我提出过用 MySQL GET_LOCK 做一个修复方案,后来没部署——因为要先做负载测试,不然这个锁可能是更大的瓶颈。这个 bug 至今还挂在我的生产环境里。对,你没看错:我已经把一个带已知竞态漏洞的预约系统放上线了。
然后部署阶段还有惊喜。Easy!Appointments 在 PHP 引导阶段硬编码了 X-Frame-Options: SAMEORIGIN 头,导致页面根本无法通过 iframe 嵌入。我改了两个 PHP 文件,让预订控制器允许 iframe、同时保留管理面板原有的防护。这个补丁位于核心代码里,下次升级大概率会被覆盖,所以我把它存档成了版本化补丁。好在迁移完成后,客户数据确实存在自己服务器上,第三方脚本清零,Google 的评级也好看了。
到这里,聪明读者可能已经发现:我讲了一路的"信任不可靠",但我的部署本身就是一个信任行为——我信任了 Easy!Appointments 大部分代码没问题,信任了维护者在过去十年里至少修过大部分高危漏洞,信任了 PHP + MySQL 这个技术栈不会明天就没人维护。自托管没有消灭信任,它只是把信任从一个 SaaS 公司的 SLA 转移到了一个开源项目的 commit 历史和维护者的业余时间上。
这是关于"谁买单、谁接刀"的问题。把格局扩开看,这局里有四方。开源维护者是这局里唯一付出劳动但主要回报是声誉资本的人——这类项目赚的是 GitHub 星标和简历上的名字,收入和贡献完全不匹配;商业 SaaS 靠免费层获客、锁定期收割,是这局里唯一明明白白赚到钱的;云厂商在中间搭了个舞台——他们乐见开源繁荣,因为开源项目的流量会变成他们平台的资源消耗账单;而最终用户(我和我那些需要预约的客户)是买单的人,只是买单的方式从"付订阅费"变成了"付维护时间 + 承担安全风险"。我选择了后者,不是因为后者更便宜,是因为我付得起这个时间的代价,而且我宁可付出自己的时间也不愿付给一个我无法审计的第三方。
我得诚实说一句:这篇文章里我做的最有判断力的一件事,不是审计出 TOCTOU,也不是部署成功。是我承认了一个事实——我的生产环境里至今带着一个未修复的竞态漏洞,而这个漏洞,大部分用商业 SaaS 的人不但不用面对,连想遇到都遇不到。自托管不提供安全性。这是它和商业 SaaS 最大的区别,也是最容易被神话覆盖的事实。自托管提供的是可见性——你能看到自己的漏洞,这本身是一种进步,但看到不等于修好。
所以我的判断摆在这儿:开源项目的声誉、年龄和付费层,不能替代实际阅读代码进行安全验证。这个判断的反面也一样成立——你不能因为"只是托管在自己的服务器上"就认为它更安全。判断会错,条件是在外部——比如哪天 Cal.diy 被某个大厂接管,获得了全职维护团队和系统性的安全审计,那我的判断就得修正。但在那之前,我不改口:自托管这件事本身不构成安全,它只构成责任——而责任这个东西,比订阅费贵多了。
评论
还没有评论,写下第一条讨论。