评论区一直是坏的,干脆在 Cloudflare 上给 Waline 重写了个后端
博客的评论区终于能用了。前台还是官方的 Waline 评论框,样子和原来基本一样(只是关掉了传图和 GIF 搜索);后端是自己照着官方接口,在 Cloudflare Workers + D1 上重写的一套,官方的管理后台也一起放在了 Cloudflare 上。从头到尾没有用一台服务器,也没有用 Vercel、LeanCloud 这些平台。这篇记录一下为什么要这么折腾、整体是怎么搭的,以及中间踩的一堆坑。

写在前面
博客文章底下那个评论框,其实一直是坏的(所以也没有旧评论要搬,倒是省事了)。原来配的评论地址,指向的是一个别人托管的 Waline 服务,那个地址早就 404 了,评论框能显示,但是发不出去,也加载不出任何东西。
主题作者 vvhan 大佬写过一篇《Astro 添加 Waline 评论组件》,讲的是前端怎么接进来,后端地址留了个空,让大家自己填。他自己是把官方服务端部署在 Vercel 上的,官方文档的快速上手教程用的也是 Vercel。
本来照着官方文档部署一个就完事了,但是我有两个小要求:不想再多养一台服务器(手上的服务器以后都打算清掉),也不想为了一个评论,再去多注册一个平台。博客本身就在 Cloudflare Pages 上,评论最好也放在 Cloudflare。
然鹅,翻了一圈 Waline 的官方文档,部署方式里有 Vercel、Netlify、Railway、Zeabur,还有国内的几家云函数,就是没有 Cloudflare。数据库那一页也没有 D1。去 GitHub 的讨论区翻了翻,维护者也回复过好几次,大意是官方服务端是 Node 写的,和 Workers 不太适配,暂时不支持。
没办法,那就自己写一个吧。
官方的为什么搬不上去
一开始也想过,能不能把官方服务端直接塞进 Workers 里,改改就能跑。挨个看了一遍它用到的东西,发现真不行:
- 过滤评论:靠的是一个在 Node 里模拟浏览器页面的库,到了 Workers 里跑不起来
- 邮件模板:要临时生成代码来跑,Workers 直接不让
- 发邮件:走的是 Node 那一套网络接口,Workers 里要另外适配,反正只是给自己发个提醒,干脆用 Cloudflare 自带的发信
- 加密口令:算一次就能把免费版每次 10ms 的 CPU 用光
(还有几条更细的,就不一个个列了)
所以最后的办法是:官方前端和官方后台一行代码都不改,我把它们会发出去的每一个请求,从源码里一条一条理出来,照着接口在 Workers 上重写一遍。数据表名、字段名都沿用官方的,以后真想换回官方,导出导入也方便。
整体是怎么搭的
画个简单的图吧:
浏览器(博客文章页,官方 @waline/client)
│ 请求 www.code410.com/waline/*
▼
Cloudflare Pages(博客本身)
│ 一个 13 行的 Pages Function,原样转发
▼ Service Binding(不走公网)
Cloudflare Worker(评论后端,自己写的)
├─ D1:存评论、计数、用户
├─ R2:放一份 IP 归属地库,查到市
├─ Email Routing:有新评论给站长发邮件
└─ Static Assets:托管官方管理后台评论接口没有单独开子域名,而是挂在博客自己的 /waline 下面,由那个 13 行的转发交给 Worker。之所以这么绕,就是为了省事:浏览器眼里这就是同一个网站,不用折腾跨域;Worker 自己没有对外的地址,只能从博客这边进来;访客的真实 IP 也能原样带过去,限频和归属地都靠它。
代价就是后台和博客共用浏览器的存储,后面有个坑就是这么来的。
图里这些全是 Cloudflare 的免费额度,误删了数据,D1 还能退回 7 天内的任意时间。严格来说也不是 100% 只有 Cloudflare:每天凌晨的备份,是靠 GitHub Actions 把数据库导出来存到私有仓库的;头像用的是 Cravatar(QQ 邮箱用 QQ 头像),表情包从 npmmirror 加载。不过这些都是现成的公共服务,不用自己维护任何东西。
自己加的几个小功能
既然是自己写的,那就加点自己想要的东西:
- 归属地显示到市:大陆的 IPv4 查 R2 里那份 IP 库(每次只读需要的那几段,不用整个读进内存),港澳台、国外和 IPv6 用 Cloudflare 自带的地理信息。Cloudflare 给的城市名是英文拼音,所以又做了一张地级市拼音对照表,像重庆、厦门、六安这种多音字,还有乌鲁木齐、呼和浩特这种不按拼音拼的,只能一个一个手动补(眼睛都看花了)
- 新评论先审核:访客发的评论先进「待审核」,同时马上给我发一封邮件提醒;我去后台看过、觉得没问题点了通过,评论才会显示出来
- 防刷:同一个 IP 短时间内发太多会被限住,内容查重,支持违禁词(命中直接进垃圾箱),冒充站长的昵称和邮箱直接拦下
- 每天自动备份:凌晨三点半导出一份,有变化才提交,提交的时候带上跳过构建的标记,不会触发博客重新部署
另外,对着官方接口一个个重写的时候,发现官方服务端也有几个小毛病,我这版就一起避开了。比如违禁词里填一个 c++,所有访客发评论都直接 500(正则没转义);Win11 显示成「Windows 11.0」;楼中楼的回复没有排序;删楼中楼里的一条回复,只会连带删掉直接回它的那一层,再往下的就成了孤儿。
踩过的坑
这次的坑是真的多,挑几个印象深的说说。
- 10ms 的 CPU 限制:免费版每次请求只有 10ms 的 CPU,一页几十条评论,要是每次打开都现场排一遍版,很容易就超了。所以改成发评论的时候就排好版、过滤好,存进数据库,读的时候直接给(中间还把一条五千字代码评论的处理时间,从十几毫秒压到了一毫秒左右,办法是先过滤再上色,太长的代码干脆不上色)
- 过滤评论的库会“装死”:网上常见的那个过滤库,到了 Workers 里不报错,而是把评论原样放过去,等于啥也没过滤。最后换了一个,规则卡得很严,拿 35 个常见的坏样本在浏览器里试了一遍,一个都没跑起来
- 开了审核,待审核的评论还是直接显示出来了:官方前端发完评论,会把服务器返回的评论直接插进页面,根本不看它是不是待审核。所以现在待审核的评论,返回给前台的内容换成了一句提示:评论已提交,站长审核通过后才会公开显示。刷新一下这句提示就没了,评论还在后台等我审核,别人从头到尾看不到
- 后台和评论区的登录对不上:同一个标签页里先登录过后台,再到评论区点「登录」,弹出来的窗口直接进了管理页,评论区却还是没登录。查下来是官方后台里一个大小写的笔误,该读大写的地方读成了小写,拿到的是空值(对,就是一个大小写的事)。官方后台的代码不想动,只好在它加载之前替它把这一步补上。反过来也有问题:在评论区点了退出,后台其实还登录着,再点登录又被自动登了回去,现在退出的时候会连后台的登录一起清掉
- 修好了,但是没生效:上面那个修复上线以后,在我自己的浏览器里还是不行。查了半天才发现,Cloudflare 的「浏览器缓存 TTL」设的是 4 个小时,它把我特意设的“每次都先确认一下”改成了“缓存 4 小时”,浏览器一直用的是旧文件。最后在引用这些文件的时候带上版本号,文件一改地址就变,这才算真正修好
- 后台的下拉菜单是透明的:官方后台的样式把下拉菜单的背景设成了透明,一点开就能看到后面的字叠在一起,补了一行 CSS 改成白底(总算碰上个简单的)
除了这些,上线前还专门从安全的角度来回过了几遍,该收紧的地方都收紧了,细节就不展开说了。
第 5 个坑最冤,记一下:在 Cloudflare 上改了 js 或 css 却迟迟不生效,可以先去这个域名的「缓存 → 配置」里看看「浏览器缓存 TTL」。源站给的缓存时间比它短的时候(包括 no-cache),它会直接改成自己的时长。
后来才发现的事
写完上线以后,我又去翻了一下资料,结果发现:Waline 的官方文档在今年 4 月,已经在「生态」页里收录了一个社区重写的 Workers 版本,叫 Waline_On_Worker,用的也是 D1。Waline 的维护者 10 月初也提了一个官方 Workers 版的合并请求,只是目前还没合并。
动手之前只顾着看讨论区,社区那边就只搜到一个几年前就停更、还标着“未完成”的版本,这两个是真没看到。所以说白了,就是重复造了个轮子吧。不过两边不太一样:那个项目功能更全,注册、第三方登录、两步验证都有;我这个只给自己的博客用,注册这些干脆砍了,多做的是同源接入、新评论邮件提醒、审核和备份这几样。
如果你也想在 Cloudflare 上用 Waline,又不想自己写,可以先去官方文档的生态页看看那个项目;等官方的 Workers 版发布了,当然是优先用官方的。
Waline 官方文档 · 生态跟官方做法比一比
| 对比项 | 官方常见做法 | 这次的做法 |
|---|---|---|
| 后端跑在哪 | Vercel、Railway 这类平台 | Cloudflare Workers |
| 数据库 | MySQL、PostgreSQL、MongoDB 等 | Cloudflare D1 |
| 评论接口地址 | 一般是单独的域名(跨域) | 博客自己的 /waline,同源 |
| 新评论通知 | 自己配 SMTP 或推送服务 | Cloudflare Email Routing |
| 管理后台 | 官方服务端自带 | Worker 托管官方后台 |
| 要不要服务器 | 不用,但要再注册一个平台 | 不用,只要一个 Cloudflare 账号 |
写在最后
从开始查资料到上线,前后十七八个小时,测试也写了一大堆(毕竟怕出事),上线当天又修了两轮,总算是稳定下来了。
线上跑了三个小时左右,一共才十几个请求,评论接口一般只用两三毫秒的 CPU;最慢的几次(冷启动、登录时算口令)超过了 10ms,好在都没报错。样本太少,说明不了什么,等访问量上来再看吧。
评论区现在开着审核,欢迎在下面留言试试,发完不会马上显示,等我审核通过就有了,别急。就这样吧。