はじめに
ランディングページは、HTML/CSS/画像を配信するだけなら静的サイトです。
しかし、問い合わせフォームを置いた瞬間に性質が変わります。フォームは外部ユーザーから入力を受け取り、botや連投に対応し、入力値を検証し、通知メールを送り、場合によっては問い合わせ内容を保存します。
つまり、LPフォームは単なるHTML部品ではなく、静的サイトに追加された小さなWeb APIです。
この記事では、Astroで作ったLPに、Cloudflare Workers + HonoでフォームAPIを追加する構成を整理します。
静的LP:
Astro
フォームAPI:
Cloudflare Workers + Hono
bot対策:
Cloudflare Turnstile
連投対策:
Cloudflare Rate Limiting
保存:
D1 / KV / R2
通知:
Cloudflare Email Service
料金や無料枠は変更される可能性があります。この記事は2026年7月時点の公式ドキュメントを前提にしています。
想定読者
この記事は、次のような人を想定しています。
- AstroでLPを制作している人
- HonoでCloudflare Workers上にフォームAPIを作りたい人
- Googleフォームや外部フォームサービスではなく、自前フォームを検討している人
- 複数LPのフォームを共通化したい人
基本構成
Astroで静的LPを生成し、問い合わせ送信だけをHonoでAPI化します。
ユーザー
↓
Astroで生成したLP
↓
Cloudflare Workers Static Assets
↓
POST /api/contact
↓
Cloudflare Workers + Hono
↓
Turnstile検証
↓
サーバー側バリデーション
↓
D1保存 / Email Service通知 / Queues投入
HonoはCloudflare Workersと相性のよい軽量Webフレームワークです。フォームAPIのような小さなエンドポイントを作る用途では扱いやすい選択肢です。
Cloudflareで使う主なサービス
LPフォームで使うCloudflareサービスは、役割ごとに分けると整理しやすくなります。
| 役割 | サービス | 用途 |
|---|---|---|
| 静的配信 | Workers Static Assets / Pages | Astroのビルド成果物を配信する |
| API | Workers + Hono |
/api/contact を実装する |
| bot対策 | Turnstile | botによる送信を減らす |
| 連投対策 | Rate Limiting | 短時間の大量送信を制限する |
| 問い合わせ保存 | D1 | 問い合わせ本文や送信日時を保存する |
| 設定保存 | KV | フォーム定義や通知先設定を保存する |
| 添付ファイル | R2 | PDF、画像、CSVなどを保存する |
| 非同期処理 | Queues | メール送信や再試行をAPI処理から分離する |
| メール通知 | Email Service | 管理者通知や自動返信を送信する |
Cloudflare WorkersのFreeプランは1日100,000リクエスト、Paidプランは月額最低5ドルで月10,000,000リクエスト込みです。
小さく始める最小構成
小規模なLPフォームであれば、無料枠中心でも始められます。
Astro
+ Workers Static Assets
+ Workers Free
+ Hono
+ Turnstile Free
+ WAF Rate Limiting Free
+ Email Service verified destination address
この構成でできることは、次の範囲です。
- AstroでLPを静的生成する
- WorkersでフォームAPIを作る
- Turnstileでbot送信を減らす
- Rate Limitingで連投を抑える
- 管理者メールアドレスに通知する
無料枠で始めるなら、まずは次のように割り切るのが現実的です。
問い合わせ者への自動返信:
なし
管理者通知:
verified destination address宛てに送信
問い合わせ保存:
なし、または少量だけD1に保存
Turnstile Freeは20 widgetsまで利用でき、challenge / verification requests は無制限です。
Cloudflare Email Serviceでは、verified destination address宛ての送信は無料です。一方、任意の宛先への送信にはWorkers Paidが必要です。
保存するならD1、設定はKV、添付はR2
問い合わせ内容を保存するなら、D1を使います。
D1:
submissions
submission_events
spam_flags
フォーム定義や通知先など、読み取り中心の設定はKVに置けます。
KV:
form:{formId}:schema
form:{formId}:notification
customer:{customerId}:settings
添付ファイルを扱うならR2を使います。
R2:
contact-attachments/{submissionId}/{fileName}
ただし、問い合わせを保存する場合は、技術的に保存できるかだけでは不十分です。
- 保存期間
- 削除依頼への対応
- 管理画面の権限
- 本番データの扱い
- ログに個人情報を残しすぎない設計
ここまで含めて、フォームの運用責任として扱う必要があります。
TurnstileとRate Limitingの役割
TurnstileとRate Limitingは、どちらもフォームの悪用対策に使えますが、役割は違います。
| 対策 | 役割 |
|---|---|
| Turnstile | botらしい送信を減らす |
| Rate Limiting | 短時間に送りすぎるリクエストを制限する |
| バリデーション | 入力値の形式・長さ・必須項目を検証する |
| ログ | 異常をあとから確認できるようにする |
Turnstileはフロントにウィジェットを置くだけでは不十分です。サーバー側でtokenを検証する必要があります。
Rate Limiting Rulesは、条件に一致するリクエストの回数を数え、上限に達したときにBlockやChallengeなどのアクションを行う仕組みです。
LPフォームでは、たとえば次のように考えます。
対象:
POST /api/contact
制限:
同じIPから短時間に何度も送られたら制限
アクション:
Managed Challenge
または Block
ただし、Rate Limitingだけで細かい制御をすべて担うのは難しいです。IP共有環境では巻き込みが起きる可能性があります。フォームIDや顧客ID単位で制御したい場合は、Workers側の実装も検討します。
Workers Paidを検討する境界
無料枠で作れるかどうかだけで構成を決めると、運用責任を見落とします。
次の要件が出てきたら、Workers Paidを前提にしたほうが設計しやすくなります。
- 複数LPを運用する
- 複数顧客のフォームを管理する
- 問い合わせ履歴を保存する
- 顧客別に通知先を変える
- 問い合わせ者へ自動返信する
- メール送信失敗時に再試行する
- 添付ファイルを受け取る
- 管理画面を作る
- 監査ログが必要になる
- 削除依頼に対応する
実務寄りの構成は、たとえば次のようになります。
Astro
+ Workers Static Assets
+ Workers Paid
+ Hono
+ Turnstile
+ Rate Limiting
+ D1
+ KV
+ Queues
+ Email Service
Queuesを使うと、フォーム送信APIとメール送信処理を分離できます。
POST /api/contact
↓
Turnstile検証
↓
バリデーション
↓
D1に保存
↓
Queuesに通知ジョブ投入
↓
レスポンスを返す
↓
Queue ConsumerでEmail Service送信
APIは問い合わせを受け付ける責務に集中し、メール送信や再試行はQueue Consumer側に分離します。
パターン別の選び方
パターンA:売り切りLP
Astro
+ Workers Static Assets
+ Workers Free
+ Hono
+ Turnstile Free
+ Email Service verified destination address
向いているケース。
- LPが1本
- 問い合わせ数が少ない
- 保存不要
- 管理者通知だけでよい
- 自動返信不要
- 保守範囲を小さくしたい
パターンB:保守付きLP
Astro
+ Workers Static Assets
+ Workers Free / Paid
+ Hono
+ Turnstile
+ Rate Limiting
+ D1
+ Email Service
向いているケース。
- 問い合わせ履歴を保存したい
- スパム状況を確認したい
- 通知先変更に対応したい
- 保守契約がある
パターンC:複数LP共通基盤
Astro
+ Workers Paid
+ Hono
+ Turnstile
+ Rate Limiting
+ D1
+ KV
+ Queues
+ Email Service
向いているケース。
- 複数案件を扱う
- 顧客別に通知先を変える
- 問い合わせ履歴を一元管理する
- メール送信失敗時に再試行したい
- 管理画面を作りたい
この段階から、単なるフォーム実装ではなく、内部ミニSaaSとして考える価値が出てきます。
パターンD:添付ファイルあり
Astro
+ Workers Paid
+ Hono
+ Turnstile
+ Rate Limiting
+ D1
+ R2
+ Queues
+ Email Service
向いているケース。
- 採用LP
- 資料添付フォーム
- PDFや画像のアップロード
- CSVエクスポート
- バックアップ保存
添付ファイルを扱う場合は、ファイルサイズ上限、拡張子チェック、MIME typeチェック、保存期間、ダウンロード権限まで設計します。
Hono側の処理順
Honoのエンドポイントは、次の順番で組み立てると責務が整理しやすくなります。
app.post('/api/forms/:formId/submissions', async (c) => {
const formId = c.req.param('formId')
const body = await c.req.json()
// 1. Turnstile tokenを検証する
// 2. formIdからフォーム定義を取得する
// 3. サーバー側で入力値を検証する
// 4. Rate Limitや重複送信を確認する
// 5. 必要ならD1に保存する
// 6. 必要ならQueuesに通知ジョブを投入する
// 7. 結果を返す
return c.json({ ok: true })
})
ブラウザ側の required や maxlength はUXのためのものです。
攻撃者はブラウザを経由せずに直接APIへPOSTできます。したがって、セキュリティ境界はHono側、つまりサーバー側に置く必要があります。
まとめ
Astro + Hono + Cloudflareを使うと、LPフォームは小さく始められます。
無料枠中心なら、次の構成が現実的です。
Astro
+ Workers Static Assets
+ Workers Free
+ Hono
+ Turnstile Free
+ WAF Rate Limiting Free
+ Email Service verified destination address
一方で、問い合わせ保存、管理画面、自動返信、添付ファイル、複数顧客対応まで広げるなら、Workers Paidを前提に、D1、KV、R2、Queues、Email Serviceを組み合わせる設計になります。
LPフォームは、単なるHTML部品ではありません。外部から入力を受け取り、botや連投に対応し、個人情報を扱い、通知メールを送り、場合によっては問い合わせ履歴を保存する小さなWebアプリです。
Cloudflareのサービス選択は料金表だけでは決まりません。
無料枠で作れるかではなく、どこまでの運用責任を引き受けるかを基準に構成を選ぶべきです。