Insights / 技术解读
只用 Cloudflare 为 Astro 博客添加评论功能
不依赖外部评论服务,只使用 Cloudflare Pages Functions、D1、Turnstile 和 Wrangler 配置,为 Astro 博客实现评论功能的实践记录。

静态网站一旦需要评论功能,就会遇到状态保存和防 spam 的问题。
Acecore 没有引入外部评论 SaaS 或嵌入式 widget,而是在 PR #101 中用 Cloudflare 内的组件完成了实现。
- Astro 显示评论 UI
- Cloudflare Pages Functions 提供
/api/comments - Cloudflare D1 保存评论
- Cloudflare Turnstile 保护投稿
wrangler.jsonc定义COMMENTS_DBbinding
重点是:评论栏不是页面里的第三方孤岛,而是站点现有 Cloudflare 架构的一部分。
结构
实现范围很小。
| 层 | 文件或服务 |
|---|---|
| UI | src/components/BlogComments.astro |
| 页面位置 | src/views/BlogPostPage.astro |
| API | functions/api/comments.ts |
| 保存 | D1 binding COMMENTS_DB |
| bot 对策 | Cloudflare Turnstile |
| schema | migrations/0001_create_blog_comments.sql |
UI 通过 GET /api/comments?slug=...&locale=... 读取评论,并通过 POST /api/comments 投稿。
Pages Function 在写入前验证 origin、payload、Turnstile、rate limit、重复投稿和禁止内容。
为什么使用 D1
评论数据需要按文章查询、按时间排序、soft delete、重复检测和限流统计。D1 可以用 SQL 很直接地表达这些操作。
显示时只取 deleted_at IS NULL 的行。遇到 spam 时,不需要物理删除,只要给 deleted_at 写入时间即可隐藏。
查询使用 prepare(...).bind(...),避免把用户输入直接拼接到 SQL 里。
Wrangler 管理绑定
wrangler.jsonc 中定义 COMMENTS_DB。
COMMENTS_DB 指向唯一的 D1 数据库 acecore-comments,这样 Cloudflare dashboard 和仓库中的数据库名称保持一致。
Cloudflare Pages 文档也说明,使用 Wrangler 配置时,它会成为 Pages project configuration 的 source of truth。
Turnstile 必须服务端验证
前端显示 Turnstile widget 只是第一步。
投稿时,Pages Function 会把 token 和 secret key 发送到 Cloudflare Siteverify API。验证结果成功后,才进入保存流程。
实现还会检查 Siteverify 返回的 hostname 是否在 allowlist 中。Cloudflare 的 Any Hostname 文档也要求在 server-side code 中验证 hostname。
spam 对策
第一版故意保持严格。
拒绝的内容包括:
- URL
- 邮箱地址
- HTML 标签
- Markdown 链接
- 过度重复字符
- 常见宣传词
- honeypot 字段
限流也分两层:内存中的短期限流,以及 D1 中基于 client_hash 的持久限流。client_hash 使用 salt 后的 SHA-256,不保存原始 IP。
SEO 与搜索
评论区域标记为 data-pagefind-ignore,评论内容通过客户端读取。因此评论不会成为文章正文的一部分,也不会进入 Pagefind 索引。
企业博客中,正文是审核过的内容,评论是互动内容。两者分开管理更安全。
总结
外部评论服务很方便,但不是唯一选择。
如果网站已经运行在 Cloudflare Pages 上,那么 Pages Functions、D1、Turnstile 和 Wrangler 就足以构成一个轻量评论系统。
这样可以把 UI、数据、安全边界和基础设施配置都放在同一个 Cloudflare 运维模型中。
参考
Cloudflare Comments
仅使用Cloudflare构建评论功能的架构
Astro负责渲染UI,Cloudflare Pages Functions作为API边界,并将D1和Turnstile作为Cloudflare内部组件连接起来。
在Astro中放置UI
在文章详情下方放置评论列表、投稿表单和Turnstile widget。
由Pages Function接收
`/api/comments` 处理GET/POST/OPTIONS,并负责输入验证与CORS。
保存到D1
通过 `COMMENTS_DB` binding将评论、hash和创建时间保存到兼容SQLite的D1。
用Turnstile保护
在server-side validation Cloudflare Turnstile token,并检查hostname allowlist。
外部评论服务与Cloudflare自建方案的区别
外部评论服务
- 导入速度快,但UI、数据存储位置、条款和显示速度都依赖服务方
- 外部script或iframe容易影响文章页面的加载
- 多语言UI和与网站设计统一时容易受到限制
- 评论数据的处理、删除与迁移依赖服务规范
仅使用Cloudflare实现
- 只用Pages Functions、D1和Turnstile即可拥有API与存储位置
- 可作为Astro侧的HTML/CSS自然融入网站设计
- 可通过Wrangler配置统一D1 binding与Cloudflare侧数据库名称
- 可自行决定垃圾信息防护、删除流程和保存的个人信息范围
实现前需要决定的事项
- 不把评论交给外部服务,而是在本网站的Cloudflare架构中管理
- 以D1作为存储位置,以Cloudflare Pages Functions作为API边界
- Turnstile token必须进行server-side validation
- 在投稿前拒绝URL、邮箱、HTML、Markdown链接和宣传用语
- 使D1 database名称与COMMENTS_DB binding和Cloudflare侧保持一致
相关页面
常见问题
为什么不用外部评论服务?
外部服务导入很快,但 UI、数据保存、脚本加载、删除和迁移都会依赖服务方。这个实现把控制权留在网站和 Cloudflare 内。
D1 适合保存评论吗?
按 post_slug 查询、按 created_at 排序、soft delete、重复检测和速率限制都很适合用 D1 处理。
只在前端放 Turnstile 可以吗?
不可以。Pages Function 必须把 token 发送到 Cloudflare Siteverify,并在验证成功后再写入 D1。