Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,7 @@ Cloudflare 側で以下を設定してください。

Cloudflare Pages の Functions binding は `wrangler.jsonc` で管理します。

- production: `COMMENTS_DB` -> `acecore-comments-production`
- preview/local: `COMMENTS_DB` -> `acecore-comments-preview`
- `COMMENTS_DB` -> `acecore-comments`

Cloudflare Pages secret は以下を設定します。

Expand All @@ -231,8 +230,7 @@ Cloudflare Pages secret は以下を設定します。
D1 schema は `migrations/0001_create_blog_comments.sql` です。初回は D1 database を作成後、以下で適用します。

```bash
npx wrangler d1 execute acecore-comments-production --remote --file=./migrations/0001_create_blog_comments.sql
npx wrangler d1 execute acecore-comments-preview --remote --file=./migrations/0001_create_blog_comments.sql
npx wrangler d1 execute acecore-comments --remote --file=./migrations/0001_create_blog_comments.sql
```

スパムなどを非表示にする場合は、対象レコードの `deleted_at` に ISO 8601 の日時を入れます。`deleted_at IS NULL` のコメントのみ表示されます。
Expand Down
4 changes: 2 additions & 2 deletions src/content/blog/astro-cloudflare-site-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ checklist:
checked: true
- text: 'フォーム導線はURLパラメータで文脈を渡し、受信値は安定した分類にする'
checked: true
- text: 'コメントなど投稿データはpreviewとproductionでDBを分ける'
- text: 'コメントなど投稿データはD1の実体名とbindingを設定で明示する'
checked: true
- text: 'AI出力やユーザー投稿は、HTMLとして信頼せず許可リストで扱う'
checked: true
Expand Down Expand Up @@ -276,7 +276,7 @@ AI機能をサイトに入れるときは、モデルの精度だけでなく、
- Turnstileをserver-side validationする
- Originとhostname allowlistを見る
- URL、メール、HTML、Markdownリンク、宣伝語句を拒否する
- previewとproductionでD1を分ける
- D1の実体名とbindingをWranglerで明示する

詳しくは [CloudflareだけでAstroブログにコメント機能を作る方法](/blog/cloudflare-only-blog-comments/) にまとめています。

Expand Down
26 changes: 13 additions & 13 deletions src/content/blog/cloudflare-only-blog-comments.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: 'CloudflareだけでAstroブログにコメント機能を作る方法'
description: '外部コメントサービスに頼らず、Cloudflare Pages Functions、D1、Turnstile、Wrangler設定だけでAstroブログにコメント機能を追加した実装記録です。API設計、保存先、スパム対策、origin制御、preview/production分離、運用時の注意点まで整理します。'
description: '外部コメントサービスに頼らず、Cloudflare Pages Functions、D1、Turnstile、Wrangler設定だけでAstroブログにコメント機能を追加した実装記録です。API設計、保存先、スパム対策、origin制御、Wranglerでのbinding管理、運用時の注意点まで整理します。'
date: 2026-06-07T18:00
lastUpdated: 2026-06-07T00:00
author: gui
Expand Down Expand Up @@ -46,7 +46,7 @@ compareTable:
items:
- 'Pages Functions、D1、TurnstileだけでAPIと保存先を持てる'
- 'Astro側のHTML/CSSとしてサイトデザインへ自然に合わせられる'
- 'Wrangler設定でpreviewとproductionのD1を分けられる'
- 'Wrangler設定でD1 bindingとCloudflare側のDB名を揃えられる'
- 'スパム対策、削除、保存する個人情報の範囲を自分たちで決められる'
checklist:
title: 実装前に決めること
Expand All @@ -59,7 +59,7 @@ checklist:
checked: true
- text: 'URL、メール、HTML、Markdownリンク、宣伝語句を投稿前に拒否する'
checked: true
- text: 'preview/localとproductionのD1 bindingを分ける'
- text: 'D1 database名とCOMMENTS_DB bindingをCloudflare側と揃える'
checked: true
linkCards:
- href: /blog/cloudflare-pages-security/
Expand Down Expand Up @@ -200,7 +200,7 @@ D1 bindingと環境変数は `wrangler.jsonc` に寄せました。
"d1_databases": [
{
"binding": "COMMENTS_DB",
"database_name": "acecore-comments-preview",
"database_name": "acecore-comments",
"database_id": "...",
"migrations_dir": "./migrations",
},
Expand All @@ -210,15 +210,15 @@ D1 bindingと環境変数は `wrangler.jsonc` に寄せました。
"d1_databases": [
{
"binding": "COMMENTS_DB",
"database_name": "acecore-comments-preview",
"database_name": "acecore-comments",
},
],
},
"production": {
"d1_databases": [
{
"binding": "COMMENTS_DB",
"database_name": "acecore-comments-production",
"database_name": "acecore-comments",
},
],
},
Expand All @@ -228,9 +228,9 @@ D1 bindingと環境変数は `wrangler.jsonc` に寄せました。

Cloudflare PagesのWrangler設定は、Pages project configurationのsource of truthとして扱われます。つまり、bindingや環境変数をダッシュボードの手作業だけに散らさず、リポジトリ側でレビューできる形にできます。

ここで大事なのは、**preview/localとproductionのD1を分けること** です。
ここで大事なのは、**binding名を固定し、D1の実体名をCloudflare側とリポジトリ側で揃えること** です。

コメント機能は投稿データを持つので、preview環境で本番D1へ書き込むと事故になります。今回も `acecore-comments-preview` と `acecore-comments-production` を分け、同じ `COMMENTS_DB` binding名で環境ごとに向き先を変えています
今回はコメント用D1を1つに寄せているため、環境ごとの `COMMENTS_DB` はすべて `acecore-comments` を指します

## Turnstileはサーバー側で検証する

Expand Down Expand Up @@ -455,7 +455,7 @@ Cloudflare内に閉じると、その代わりに自分たちで作る責任は

他のAstroサイトに入れるなら、流れはこうです。

1. Cloudflare D1 databaseをpreview用とproduction用に作る
1. Cloudflare D1 databaseを作る
2. `wrangler.jsonc` に `COMMENTS_DB` bindingを追加する
3. `TURNSTILE_SECRET_KEY` と `COMMENT_HASH_SALT` をPages secretに設定する
4. `COMMENT_ALLOWED_HOSTNAMES` に本番hostとPages preview hostを入れる
Expand All @@ -464,7 +464,7 @@ Cloudflare内に閉じると、その代わりに自分たちで作る責任は
7. `functions/api/comments.ts` でGET/POST/OPTIONSを実装する
8. `BlogComments.astro` で一覧、フォーム、Turnstile widgetを実装する
9. `BlogPostPage.astro` の本文下へコメントコンポーネントを置く
10. productionとpreviewで別D1に書き込まれることを確認する
10. 投稿が想定したD1 databaseに書き込まれることを確認する

Cloudflare D1のWrangler commandでは、SQLファイルを実行する `d1 execute` や、versioned migration fileを作る `d1 migrations create` / `d1 migrations apply` が用意されています。プロジェクトの運用に合わせて、単発のschema適用にするか、migrations applyに寄せるかを決めます。

Expand All @@ -479,7 +479,7 @@ Cloudflare D1のWrangler commandでは、SQLファイルを実行する `d1 exec
- Turnstile未完了ではPOSTできない
- URLやメールアドレス入りの本文が拒否される
- 同じ本文の連投が拒否される
- production previewで本番D1へ書き込まない
- `COMMENTS_DB` が想定したD1 databaseへ書き込む
- `COMMENT_ALLOWED_HOSTNAMES` にないoriginから拒否される
- `deleted_at` を入れたコメントが表示されない

Expand Down Expand Up @@ -519,10 +519,10 @@ Astroの通常dev serverだけでは、Cloudflare Pages FunctionsやD1 binding

ただ、すでにCloudflare Pagesで配信しているサイトなら、Cloudflareだけで作る選択肢もかなり現実的です。

Pages FunctionsでAPI境界を作り、D1に保存し、Turnstileでbot対策をし、Wranglerで環境ごとのbindingを管理する。これで、静的サイトの強みを残したまま、必要な部分だけ動的にできます。
Pages FunctionsでAPI境界を作り、D1に保存し、Turnstileでbot対策をし、Wranglerでbindingを管理する。これで、静的サイトの強みを残したまま、必要な部分だけ動的にできます。

外部サービスに頼らない分、入力検証、rate limit、hostname allowlist、削除運用は自分たちで設計する必要があります。

その代わり、コメント欄のUI、データ、セキュリティ境界、preview/production分離をサイトの方針に合わせられます
その代わり、コメント欄のUI、データ、セキュリティ境界、インフラ設定をサイトの方針に合わせられます

公式サイトのブログに小さくコメント機能を足したいなら、Cloudflareだけで作る構成は十分に候補になります。
6 changes: 3 additions & 3 deletions src/content/blog/de/cloudflare-only-blog-comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Acecore hat keinen externen Kommentardienst eingebettet. In [PR #101](https://gi
- Cloudflare Pages Functions stellt `/api/comments` bereit.
- Cloudflare D1 speichert Kommentare.
- Cloudflare Turnstile schützt POST-Anfragen.
- `wrangler.jsonc` trennt preview und production.
- `wrangler.jsonc` definiert das `COMMENTS_DB`-Binding.

Der Vorteil: Der Kommentarbereich bleibt Teil der bestehenden Cloudflare-Architektur.

Expand Down Expand Up @@ -71,9 +71,9 @@ Prepared Statements mit `bind()` verhindern, dass Benutzereingaben direkt in SQL

## Wrangler als Vertrag

`COMMENTS_DB` wird in `wrangler.jsonc` definiert. Preview nutzt `acecore-comments-preview`, production nutzt `acecore-comments-production`.
`COMMENTS_DB` wird in `wrangler.jsonc` definiert und zeigt auf die einzige D1-Datenbank `acecore-comments`.

So schreibt eine PR-Preview nicht in die Produktionsdatenbank.
So bleibt der Binding-Name stabil, während Dashboard und Repository denselben Datenbanknamen verwenden.

## Turnstile serverseitig prüfen

Expand Down
10 changes: 5 additions & 5 deletions src/content/blog/en/cloudflare-only-blog-comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ For Acecore, we did not add an external comment SaaS or embedded widget. The imp
- Cloudflare Pages Functions expose `/api/comments`.
- Cloudflare D1 stores comments.
- Cloudflare Turnstile protects POST requests.
- `wrangler.jsonc` defines production and preview bindings.
- `wrangler.jsonc` defines the `COMMENTS_DB` binding.

That is the main point: **the comment feature is not a third-party island inside the page**. It uses the same Cloudflare boundary as the rest of the static site.

Expand Down Expand Up @@ -86,23 +86,23 @@ Bindings are defined in `wrangler.jsonc`.
"d1_databases": [
{
"binding": "COMMENTS_DB",
"database_name": "acecore-comments-preview",
"database_name": "acecore-comments",
},
],
"env": {
"production": {
"d1_databases": [
{
"binding": "COMMENTS_DB",
"database_name": "acecore-comments-production",
"database_name": "acecore-comments",
},
],
},
},
}
```

The important part is separating preview and production. A PR preview should never write to the production comments database.
The important part is keeping the binding name stable while pointing every environment at the single D1 database, `acecore-comments`.

Cloudflare Pages documentation treats Wrangler configuration as the source of truth for a Pages project, which is exactly what we want for reviewable infrastructure changes.

Expand Down Expand Up @@ -140,7 +140,7 @@ External comment services are convenient, but they are not the only option.

If a site already runs on Cloudflare Pages, a lightweight comment feature can live entirely inside Cloudflare: Pages Functions for the API, D1 for storage, Turnstile for abuse prevention, and Wrangler for bindings.

That keeps UI, data, security boundaries, and preview/production separation under the same operational model as the site itself.
That keeps UI, data, security boundaries, and infrastructure configuration under the same operational model as the site itself.

## References

Expand Down
6 changes: 3 additions & 3 deletions src/content/blog/es/cloudflare-only-blog-comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ En Acecore no usamos un servicio externo de comentarios. En [PR #101](https://gi
- Cloudflare Pages Functions expone `/api/comments`.
- Cloudflare D1 guarda los comentarios.
- Cloudflare Turnstile protege el POST.
- `wrangler.jsonc` separa preview y production.
- `wrangler.jsonc` define el binding `COMMENTS_DB`.

La ventaja es que el comentario no queda como una isla de terceros dentro de la página.

Expand Down Expand Up @@ -75,9 +75,9 @@ Por eso D1 es más cómodo que un almacén key-value. Además, las consultas usa

## Configuración por Wrangler

El binding `COMMENTS_DB` se define en `wrangler.jsonc`. Preview apunta a `acecore-comments-preview`; production apunta a `acecore-comments-production`.
El binding `COMMENTS_DB` se define en `wrangler.jsonc` y apunta a la única base D1 `acecore-comments`.

Esta separación evita que una preview de Pull Request escriba en la base real.
Así el nombre del binding se mantiene estable y el dashboard de Cloudflare coincide con el repositorio.

## Turnstile en servidor

Expand Down
6 changes: 3 additions & 3 deletions src/content/blog/fr/cloudflare-only-blog-comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Pour Acecore, nous n'avons pas utilisé de service externe de commentaires. Dans
- Cloudflare Pages Functions expose `/api/comments`.
- Cloudflare D1 stocke les commentaires.
- Cloudflare Turnstile protège les envois.
- `wrangler.jsonc` sépare preview et production.
- `wrangler.jsonc` définit le binding `COMMENTS_DB`.

Le point fort est clair : la zone de commentaires n'est pas un îlot tiers dans la page.

Expand Down Expand Up @@ -71,9 +71,9 @@ Les requêtes passent par des prepared statements et `bind()`, sans concaténer

## Wrangler comme contrat

Le binding `COMMENTS_DB` est défini dans `wrangler.jsonc`. Preview pointe vers `acecore-comments-preview`; production vers `acecore-comments-production`.
Le binding `COMMENTS_DB` est défini dans `wrangler.jsonc` et pointe vers l'unique base D1 `acecore-comments`.

C'est important : une preview de Pull Request ne doit pas écrire dans la base de production.
Cela garde le nom du binding stable tout en alignant le tableau de bord Cloudflare et le dépôt sur le même nom de base.

La documentation Cloudflare Pages indique aussi que la configuration Wrangler devient la source de vérité du projet Pages.

Expand Down
6 changes: 3 additions & 3 deletions src/content/blog/ko/cloudflare-only-blog-comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Acecore는 외부 댓글 SaaS나 widget을 쓰지 않았습니다. [PR #101](htt
- Cloudflare Pages Functions가 `/api/comments`를 제공합니다.
- Cloudflare D1이 댓글을 저장합니다.
- Cloudflare Turnstile이 POST를 보호합니다.
- `wrangler.jsonc`가 preview와 production을 나눕니다.
- `wrangler.jsonc`가 `COMMENTS_DB` binding을 정의합니다.

핵심은 댓글 영역이 페이지 안의 외부 서비스가 아니라, 기존 Cloudflare 구성의 일부라는 점입니다.

Expand Down Expand Up @@ -71,9 +71,9 @@ Function은 origin, payload, Turnstile, rate limit, 중복, 금지 내용을 검

## Wrangler로 환경을 나누기

`COMMENTS_DB`는 `wrangler.jsonc`에 정의합니다. preview는 `acecore-comments-preview`, production은 `acecore-comments-production`을 사용합니다.
`COMMENTS_DB`는 `wrangler.jsonc`에 정의하며, 단일 D1 database인 `acecore-comments`를 가리킵니다.

PR preview가 production DB에 쓰지 않도록 하는 것이 중요합니다.
binding 이름은 유지하고 Cloudflare dashboard와 repository의 database 이름을 맞춥니다.

## Turnstile은 서버에서 검증

Expand Down
6 changes: 3 additions & 3 deletions src/content/blog/pt/cloudflare-only-blog-comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ No Acecore, escolhemos outro caminho: em [PR #101](https://github.com/acecore-sy
- Cloudflare Pages Functions expõe `/api/comments`.
- Cloudflare D1 armazena comentários.
- Cloudflare Turnstile protege o envio.
- `wrangler.jsonc` separa preview e production.
- `wrangler.jsonc` define o binding `COMMENTS_DB`.

O ponto principal é que a área de comentários não vira uma dependência externa dentro da página.

Expand Down Expand Up @@ -71,9 +71,9 @@ As queries usam prepared statements com `bind()`, evitando concatenar input de u

## Wrangler e ambientes

`COMMENTS_DB` fica em `wrangler.jsonc`. Preview aponta para `acecore-comments-preview`; production aponta para `acecore-comments-production`.
`COMMENTS_DB` fica em `wrangler.jsonc` e aponta para o único banco D1 `acecore-comments`.

Isso evita que uma preview de PR grave dados reais de produção.
Assim o nome do binding continua estável, enquanto o dashboard da Cloudflare e o repositório usam o mesmo nome de banco.

## Turnstile no servidor

Expand Down
6 changes: 3 additions & 3 deletions src/content/blog/ru/cloudflare-only-blog-comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Acecore не стал подключать внешний сервис комм
- Cloudflare Pages Functions предоставляет `/api/comments`.
- Cloudflare D1 хранит комментарии.
- Cloudflare Turnstile защищает POST.
- `wrangler.jsonc` разделяет preview и production.
- `wrangler.jsonc` задает binding `COMMENTS_DB`.

Главное преимущество: комментарии не становятся сторонним виджетом внутри страницы.

Expand Down Expand Up @@ -71,9 +71,9 @@ Function проверяет origin, payload, Turnstile, лимиты, дубли

## Wrangler и окружения

`COMMENTS_DB` задается в `wrangler.jsonc`. Preview пишет в `acecore-comments-preview`, production — в `acecore-comments-production`.
`COMMENTS_DB` задается в `wrangler.jsonc` и указывает на единственную D1-базу `acecore-comments`.

Это защищает production от случайных записей из PR preview.
Так имя binding остается стабильным, а Cloudflare dashboard и репозиторий используют одно имя базы.

## Turnstile на сервере

Expand Down
6 changes: 3 additions & 3 deletions src/content/blog/zh-cn/cloudflare-only-blog-comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Acecore 没有引入外部评论 SaaS 或嵌入式 widget,而是在 [PR #101](
- Cloudflare Pages Functions 提供 `/api/comments`
- Cloudflare D1 保存评论
- Cloudflare Turnstile 保护投稿
- `wrangler.jsonc` 区分 preview 和 production
- `wrangler.jsonc` 定义 `COMMENTS_DB` binding

重点是:评论栏不是页面里的第三方孤岛,而是站点现有 Cloudflare 架构的一部分。

Expand Down Expand Up @@ -75,7 +75,7 @@ Pages Function 在写入前验证 origin、payload、Turnstile、rate limit、

`wrangler.jsonc` 中定义 `COMMENTS_DB`。

preview 使用 `acecore-comments-preview`,production 使用 `acecore-comments-production`。这样 PR preview 不会误写 production 数据库
`COMMENTS_DB` 指向唯一的 D1 数据库 `acecore-comments`,这样 Cloudflare dashboard 和仓库中的数据库名称保持一致

Cloudflare Pages 文档也说明,使用 Wrangler 配置时,它会成为 Pages project configuration 的 source of truth。

Expand Down Expand Up @@ -115,7 +115,7 @@ Cloudflare Pages 文档也说明,使用 Wrangler 配置时,它会成为 Page

如果网站已经运行在 Cloudflare Pages 上,那么 Pages Functions、D1、Turnstile 和 Wrangler 就足以构成一个轻量评论系统。

这样可以把 UI、数据、preview/production 分离和安全边界都放在同一个 Cloudflare 运维模型中。
这样可以把 UI、数据、安全边界和基础设施配置都放在同一个 Cloudflare 运维模型中。

## 参考

Expand Down
12 changes: 6 additions & 6 deletions wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
"d1_databases": [
{
"binding": "COMMENTS_DB",
"database_name": "acecore-comments-production",
"database_id": "10bb4e84-2c31-427f-931c-fcb123c57501",
"database_name": "acecore-comments",
"database_id": "3f31c39c-e418-4460-8651-4385e0409157",
"migrations_dir": "./migrations",
},
],
Expand All @@ -40,8 +40,8 @@
"d1_databases": [
{
"binding": "COMMENTS_DB",
"database_name": "acecore-comments-production",
"database_id": "10bb4e84-2c31-427f-931c-fcb123c57501",
"database_name": "acecore-comments",
"database_id": "3f31c39c-e418-4460-8651-4385e0409157",
"migrations_dir": "./migrations",
},
],
Expand All @@ -62,8 +62,8 @@
"d1_databases": [
{
"binding": "COMMENTS_DB",
"database_name": "acecore-comments-production",
"database_id": "10bb4e84-2c31-427f-931c-fcb123c57501",
"database_name": "acecore-comments",
"database_id": "3f31c39c-e418-4460-8651-4385e0409157",
"migrations_dir": "./migrations",
},
],
Expand Down