Insights / 技术解读

只用 Cloudflare 为 Astro 博客添加评论功能

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

  • 技术
  • Cloudflare
  • Astro
  • 安全
  • 网站
只用 Cloudflare 为 Astro 博客添加评论功能
本文目录
  1. 结构
  2. 为什么使用 D1
  3. Wrangler 管理绑定
  4. Turnstile 必须服务端验证
  5. spam 对策
  6. SEO 与搜索
  7. 总结
  8. 参考

是否自建博客评论,应依据能否承担审核与删除,而不只是存储选择。希望委托管理可选外部服务,需要独立UI和存储策略可考虑D1。首次投稿测试应按Cloudflare Turnstile: Server-side Validation确认验证失败不会写入数据库。

静态网站一旦需要评论功能,就会遇到状态保存和防 spam 的问题。

Acecore 没有引入外部评论 SaaS 或嵌入式 widget,而是在 PR #101 中用 Cloudflare 内的组件完成了实现。

  • Astro 显示评论 UI
  • Cloudflare Pages Functions 提供 /api/comments
  • Cloudflare D1 保存评论
  • Cloudflare Turnstile 保护投稿
  • wrangler.jsonc 定义 COMMENTS_DB binding

重点是:评论栏不是页面里的第三方孤岛,而是站点现有 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内部组件连接起来。

  1. 在Astro中放置UI

    在文章详情下方放置评论列表、投稿表单和Turnstile widget。

  2. 由Pages Function接收

    `/api/comments` 处理GET/POST/OPTIONS,并负责输入验证与CORS。

  3. 保存到D1

    通过 `COMMENTS_DB` binding将评论、hash和创建时间保存到兼容SQLite的D1。

  4. 用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。