問い合わせ フォームを Cloudflare Workersで 実装する: Turnstile・ Resend 連携
まず押さえたいことCloudflare Workersとは、Cloudflareのネットワーク上でコードを実行するサーバーレス環境です。静的ファイルの配信だけでは、メール送信のようなサーバー側の処理は実行されません。フォームの受信処理をWorkersやPages Functionsに受け持たせると、専用のサーバーを常時稼働させずに実装できます。
この記事の要点
静的サイトのフォームは、ブラウザからメールAPIを直接呼ばず、WorkersやPages Functionsなどのサーバー側処理で入力検証、トークンの検証、送信、記録を行います。Turnstileは対策の一つであり、レート制限、失敗時の表示と通知、ログの保存期間も合わせて設計します。メールAPIの受付と受信側への配信は分けて確認し、採用するサービスの料金と上限は導入時に確認します。
この 記事で わかる こと
- なぜ Pages だけでは
“問い合わせ フォーム”が 難所なのか - この
記事の 前提:Workersで 実装する - アーキテクチャ
(Bot排除と メール送信) - 実装手順
(番号付きガイド) - 運用設計
(脅威モデルと 対策) - 公開前に
確認する 送信・障害時の 動作 - Vercel と
比較 (開発者目線) - 発展:WAF / CDN / Zero Trust への
布石 - まとめ
- よく
ある 質問 (FAQ) - 参考
リンク / 出典
なぜ Pages だけでは “問い合わせ フォーム”が 難所なのか
Cloudflare Pagesに置いたHTMLやCSSなどの静的ファイルは、そのまま配信されます。静的ファイルの配信だけでは、「メールを送る」「データベースに保存する」といったサーバー側の処理は実行されません。PagesではPages Functionsでサーバー側のコードを動かせるため、フォームの受信処理はPages Functions、別のWorker、外部のフォームサービスのいずれかで補います。
ブラウザのJavaScriptからメールAPIを直接呼ぶと、APIキーを公開することになり、第三者に送信機能を悪用されるおそれがあります。キーを扱う処理はサーバー側に置きます。
補足:この記事は、実装と運用を中心にした技術的な解説です。静的配信で減らせる維持費と残る費用(経営者・Web担当者向け)は、ホームページ維持費の記事で扱っています。
表は
| 方式 | A. 外部の | B. Pages Functions | C. 別の |
|---|---|---|---|
| 概要 | フォームサービスの | Pages | 受信処理を |
| 選びやすい | 画面・項目が | 既存の | サイトと |
| 確認する | 見た目と | 秘密値の | 呼び出し元の |
| 運用の | サービスの | サイトと | サイトとは |
どの方式でも、入力の検証、スパム対策、通知先、失敗したときの確認は必要です。この記事では、サイトと受信処理を独立して公開・運用したい場合の構成例として、Cの別のWorkerで手順を示します。
この 記事の 前提:Workersで 実装する
この記事は、フォームの受信処理を独立したCloudflare Workerで実装する構成例です。サイトと受信処理を別々に公開・運用したい場合を想定しています。秘密値の保存やログの確認はPages Functionsでも使えるため(Pages FunctionsのSecrets・ログ)、それだけでWorkersを選ぶ理由にはなりません。
Cloudflareは、新しいプロジェクトをWorkersで始めるよう案内しています(Cloudflare Pagesの概要)。新しく作る場合はWorkersを第一の候補として検討し、既存のPagesサイトでは、Pages Functionsで受信処理を加えて運用を続ける方法もあります。構成全体の選び方はCloudflare PagesとWorkersの違い:構成を選ぶための判断表で扱っています。Workers側の認証・キャッシュ・運用の設計はCloudflare Workersの実装設計を参照してください。
アーキテクチャ (Bot排除と メール送信)
今回の
Turnstileのトークンは、Workerがサーバー側の検証(Siteverify)で確認します。検証に失敗した場合はメールAPIへ進まず、メールAPIがエラーを返した場合も送信済みとして扱いません。
メールAPIが
実装手順 (番号付きガイド)
構築時に
サイトと受信処理が別のオリジン(ドメインなど)にある場合は、Worker側でCORSを設定し、許可するオリジンを指定します。CORSはブラウザが適用する仕組みのため(MDN:CORS)、利用者の認証の代わりにはならず、ブラウザ以外からの直接の呼び出しも防げません。
自社の状況を確認する
- □ Turnstileの
Secret Keyと Resendの API Keyを 取り違えていないか - □ 秘密値が
本番の 環境に 設定され、 Workerから 読み込めているか - □ 別の
オリジンから 呼ぶ 場合、 CORSで 自社サイトの オリジンだけを 許可しているか
Cloudflare Turnstile の 発行
Resend の API Key発行と ドメイン設定
Worker プロジェクトの 作成
Secrets (環境変数)の 設定
Workerの コードの 実装 (入力の 検証と 送信)
クライアント側 (Pages)の 実装
デプロイと 動作確認
運用設計 (脅威モデルと 対策)
公開したフォームは、ボットによる送信や、送信機能の悪用の対象になります。主な脅威と対策を整理します。1つの対策だけで防げるものではないため、適用範囲を確認して組み合わせます。
表は
| 脅威 | 具体的な | 主な |
|---|---|---|
| ボットに | 広告や | Turnstileの |
| 大量送信・DoS | APIを | 多重送信の防止、レート制限(WAFのルールやWorker内の制限)。WAFのルールは対象のホスト・パスと契約プランで使える条件が異なる。通常の利用者を誤って遮断しないかを確認する |
| なりすまし | 自社の | SPF・ |
| キーの | API Keyを | 秘密値として |
Turnstileは、訪問者に画像選択などのパズルを表示せずに判定できる仕組みです(Turnstile)。判定の結果はサーバー側の検証で確認する必要があり、トークンは発行から300秒で失効し、1回しか検証できません(Siteverify)。検証のAPIに接続できない場合も、通過させずにエラーとして扱います。Turnstileだけで大量送信や不正な入力を防げるわけではないため、対策の適用範囲、レート制限、通常の利用者への影響、通知とログの確認を組み合わせます。
メールAPIが429・5xxを返した場合やタイムアウトした場合は、利用者に失敗または再試行の案内を表示し、受付済みかどうか分からない送信は照合します。同じ問い合わせの重複を防ぐには、問い合わせごとにIDを決め、それに対応するメール送信用の重複防止キー(ResendのIdempotency-Key)を、再試行でも同じ値で使います。再試行のたびに別のキーを発行すると、重複の防止になりません。Resendはこのキーを24時間保持します(Resend:Idempotency Keys、確認日:2026年10月22日)。Turnstileのトークンは人による操作かを確かめるためのもので、1回しか検証できないため、問い合わせの識別や重複の防止には使いません。
公開前に 確認する 送信・障害時の 動作
公開前に、
表は
| 確認する | 期待する | 確認する |
|---|---|---|
| 正常な | フォームの | 問い合わせIDと |
| 不正な | 処理を | 拒否した |
| トークンの | 通過させず | 検証結果の |
| メールAPIの | 成功と | APIの |
| メールAPIの | 受付済みか | 問い合わせIDと |
| 配信の | メールAPIの | Resendの |
| 連打・大量送信 | 多重送信の | 制限した |
記録には、問い合わせの本文や秘密値をそのまま残さないようにします。照合に必要なID、処理結果、時刻などに絞り、確認する担当と保存期間を決めます。ログが保存されるかどうかと保存期間は、利用するサービスと設定によって異なります。たとえば、Pages Functionsのリアルタイムのログは保存されません(Pages Functionsのログ)。配信状態は、Resendの配信イベント(送信受付・配信・遅延・バウンスなど)で確認できます(Resend:Event Types)。
Vercel と 比較 (開発者目線)
同種の
表は
| 項目 | Vercel Functions | Cloudflare Workers |
|---|---|---|
| 実行環境 | Node.jsランタイム。 | Workersの |
| 依存ライブラリ | Node.js向けの | 使う |
| 既存の | Vercelで | Cloudflareで |
| 費用の | プランと、 | プランと、 |
どちらが速いか、安いかは、処理の内容、利用量、契約プランによって変わります。メール送信だからといって一方が常に有利とは限りません。無料枠と課金条件は変更されるため、金額は各社の料金ページで最新の条件を確認してください(Vercel:Node.jsランタイム)。
発展:WAF / CDN / Zero Trust への 布石
フォームを公開した後も、Cloudflareの機能で保護を追加できます。ただし、使える機能と設定できる条件は、対象のURL・ドメイン・公開経路と契約プランによって異なります。アプリ側の入力検証やトークンの検証、運用の担当を置き換えるものではありません。
- WAF:Cloudflareを
経由する ドメインへの リクエストに、 条件に 応じた ルールを 適用します。 - レート制限:一定の時間内のリクエスト数に上限を設けます。ルールに使える条件や期間は、プランによって異なります(Rate limiting rules)。たとえば、同じ送信元から短い時間に一定の回数を超えたら一時的に遮断する、といったルールを、通常の利用者を遮断しない値で決めます。
- Zero Trust:社内向けの
管理画面を 追加で 作る 場合に、 利用者を 認証してから アクセスを 許可する 仕組みを 組み合わせられます。
どの
まとめ
静的サイトでも、フォームの受信処理をサーバー側に置けば問い合わせフォームを実装できます。受信処理はPages Functionsや別のWorkerで実装でき、画面・項目やデータの保存先がサービスの範囲で足りる場合は、外部のフォームサービスも選択肢です。どの方式でも、配信サービスの料金、コードと設定の保守、失敗したときの確認の担当は残るため、運用費と担当を含めて選びます。
自社の状況を確認する
- □ 受付の
方式 (外部 サービス・ Pages Functions・ 別の Worker)と、 公開する 経路を 決めた - □ 受付条件
(メソッド・ 形式・ サイズ)と 入力値を サーバー側で 検証している - □ Turnstileの
トークンを サーバー側で 検証し、 失敗・失効・再 使用を 通過させない - □ 送信元は
検証済みの ドメイン、 通知先は サーバー側で 決め、 秘密値を コードに 書いていない - □ メールAPIの
エラーや タイムアウトを 成功と 表示せず、 再試行と 重複防止の 方法を 決めた - □ メールAPIの
受付とは 別に、 配信状態を 確認する 方法を 決めた - □ 記録する
項目と 保存期間、 確認する 担当を 決めた - □ 公開前に、
正常な 送信と 異常時の 動作を 確認した
よく ある 質問 (FAQ)
Q. Turnstileは
Turnstileは、訪問者に画像選択などのパズルを表示せずに判定できる仕組みとして提供されています。公式の説明では、フォームの入力内容などを取得・保存しないとされています。どちらを使う場合も、トークンはサーバー側で検証します。個人データの扱いは、各サービスのプライバシー条件と自社の方針で確認してください。
Q. 無料プランで
無料枠の上限はサービスごとに異なり、変更される場合があります。通常時とキャンペーン時の送信数を見積もり、Workersとメール配信サービスの両方について、最新の料金、レート制限、上限を超えたときの動作を確認してください。
Q. Resendの
Resendで送信するには、自分が管理するドメインを追加して検証する必要があり、Gmailなどの共有のドメインは送信元に使えません。送信元は、noreplyなど、検証済みの自社ドメインのアドレスにします。利用者のメールアドレスはFromに入れず、返信先として使う場合は検証した入力をReply-Toに設定します。
Q. Pages Functionsと
既存の構成と運用の分担で選びます。既存のPagesサイトに受信処理を加え、同じプロジェクトで管理するならPages Functionsで実装できます。新しく作る場合や、サイトと受信処理を別々に公開・運用したい場合は、Workersを検討します。秘密値の保存やログの確認は、どちらでも使えます。
Q. 将来的に
スパムが増えてから考えるのではなく、公開前に必要な対策を決めておきます。サーバー側の検証、入力の制限、多重送信の防止は、公開した時点から必要です。WAFやレート制限のルールは、対象のURLと公開経路、契約プランで使える条件を確認し、通常の利用者を遮断しない設定から始めます。
参考 リンク / 出典
押さえておきたい点
参考情報
- Cloudflare Pages
(概要。 新規 プロジェクトは Workersで 始める 案内。 確認日:2026年10月22日) - Cloudflare Pages Functions
(確認日:2026年10月22日) - Pages Functions:Bindings
(Secrets。 確認日:2026年10月22日) - Pages Functions:Debugging and logging
(確認日:2026年10月22日) - Cloudflare Workers:Static Assets
(確認日:2026年10月22日) - Cloudflare Workers Documentation
- Cloudflare Turnstile Documentation
- Turnstile:Server-side validation
(Siteverify。 確認日:2026年10月22日) - Cloudflare WAF:Rate limiting rules
(確認日:2026年10月22日) - MDN:Cross-Origin Resource Sharing (CORS)
(確認日:2026年10月22日) - Resend Documentation
- Resend:Verified Domains
(確認日:2026年10月22日) - Resend:Send Email
(確認日:2026年10月22日) - Resend:Event Types
(確認日:2026年10月22日) - Resend:Idempotency Keys
(確認日:2026年10月22日) - Vercel:Using the Node.js Runtime with Vercel Functions
(確認日:2026年10月22日)
問い合わせフォームを、 送信と運用の 条件から整えます。
現在のサイトと環境、必要な入力項目、通知先、迷惑メールの対策、失敗したときに確認する担当を合意して、フォームを設計・改修する支援です。今の環境と権限を確認して、対応できるかと範囲を決めます。公開後の継続的な監視や運用の代行は、別に合意します。
この分野の全体像
モダンWeb開発の 構成判断:Next.jsと ヘッドレスCMSを どう組み合わせるか
この