EmDashで作ったサイトに、お問い合わせフォームを設置する方法を解説します。フォームに入力された内容は、メール送信サービスの「Resend(リセンド)」を使って、あなたのメールアドレスに届くようにします。あわせて、いたずら送信を防ぐためにCloudflareの「Turnstile(ターンスタイル)」も設定します。
この記事は、【公開編】Cloudflareで構築して独自ドメインで公開するまでの続きです。まだサイトを公開していない方は、先に公開編の手順を済ませてください。独自ドメインがCloudflareで管理されていることが前提になります。

フォームの仕組み
お問い合わせフォームは、次の順番で動きます。
- 訪問者がフォームに入力して「送信」を押す
- Turnstileが、送信したのが人間かロボットかを確認する
- サイトのプログラム(Cloudflare Workers)が内容を受け取る
- ResendのAPIを使って、あなたのメールアドレスにメールを送る
Cloudflare Workersは、普通のサーバーのように直接メールを送ることができません。そこで、メール送信を専門に引き受けてくれるResendを使います。
用意するもの
- 公開編で公開したEmDashのサイト
- Cloudflareで管理している独自ドメイン
- 受信用のメールアドレス(フォームの内容を受け取るアドレス)
Step 1 Resendのアカウントを作る
Resendは、プログラムからメールを送るためのサービスです。無料プランでも、1か月3,000通(1日100通)まで送信できます。お問い合わせフォームなら十分な量です。
- Resendの公式サイトを開き、「Get Started」からアカウントを作ります
- メールアドレスで登録した場合は、届いた確認メールのリンクを開きます
注意:Macでは、ResendのサイトをChromeなどで開くと、ボタンが表示されないことがあります。私たちが設定したときも、登録やドメイン追加のボタンが出てこず、先に進めませんでした。そのときはSafariで開き直すと、ボタンが表示されて操作できました。画面が途中で止まったように見えたら、まずSafariで試してみてください。
Step 2 Resendに独自ドメインを登録する
「info@example.com」のように、独自ドメインのアドレスからメールを送れるようにします。この設定をしないと、迷惑メールと判定されやすくなります。
- Resendの管理画面で「Domains」を開き、「Add Domain」を押します
- 独自ドメイン(例:example.com)を入力して追加します
- DNSに追加する設定(レコード)がいくつか表示されます。メールの送信元を証明するためのSPFやDKIMといった設定です
表示された設定を、Cloudflareに追加します。
- Cloudflareのダッシュボードで独自ドメインを選び、「DNS」の「レコード」を開きます
- Resendに表示された種類(TXT、MXなど)、名前、値を、1つずつそのまま追加します
- 追加が終わったら、Resendの画面で「Verify」を押します
しばらくしてステータスが「Verified」になれば完了です。反映に時間がかかることもあるので、すぐに変わらなくても少し待ってから確認してください。
Step 3 ResendのAPIキーを発行して登録する
APIキーは、あなたのサイトがResendを使うための「合い言葉」です。
- Resendの管理画面で「API Keys」を開き、「Create API Key」を押します
- 名前(例:my-site)を付け、権限は「Sending access」を選んで作成します
- 表示されたキー(re_で始まる文字列)をコピーします。キーはこの画面でしか表示されません
コピーしたキーを、Cloudflareに「シークレット」として登録します。プロジェクトのフォルダで次のコマンドを実行し、聞かれたらキーを貼り付けてEnterキーを押します。
npx wrangler secret put RESEND_API_KEY注意:APIキーは、プログラムのファイルに直接書いたり、人に見せたりしないでください。キーを知られると、あなたのドメインから勝手にメールを送られてしまいます。万が一漏れてしまったら、Resendの管理画面でそのキーを削除し、新しいキーを発行し直してください。
Step 4 Turnstileでいたずら送信を防ぐ
フォームをそのまま公開すると、ロボットによる大量のいたずら送信が届くことがあります。CloudflareのTurnstileを使うと、画像を選ばせるような面倒な確認なしで、人間かどうかを自動で判定してくれます。
- Cloudflareのダッシュボードで「Turnstile」を開き、「ウィジェットを追加」を押します
- 名前を付け、ホスト名に独自ドメイン(例:example.com)を追加します
- 作成すると「サイトキー」と「シークレットキー」が表示されます
サイトキーは画面に埋め込む公開用のキーなので、wrangler.jsonc の vars に書いてかまいません。シークレットキーは、APIキーと同じようにシークレットとして登録します。
npx wrangler secret put TURNSTILE_SECRET_KEYStep 5 フォームのプログラムを追加する
ここからはプロジェクトにファイルを2つ追加します。まず、wrangler.jsonc の vars に、送信元・送信先のアドレスとTurnstileのサイトキーを書きます。
"vars": {
"EMDASH_SITE_URL": "https://example.com",
"CONTACT_FROM_EMAIL": "お問い合わせ <info@example.com>",
"CONTACT_ADMIN_EMAIL": "you@example.com",
"TURNSTILE_SITE_KEY": "ここにサイトキー"
}CONTACT_FROM_EMAIL は、Step 2でResendに登録したドメインのアドレスにします。CONTACT_ADMIN_EMAIL は、フォームの内容を受け取りたいアドレスです。
フォームを受け取ってメールを送るプログラム
src/pages/api/contact.ts というファイルを作り、次の内容を貼り付けます。Turnstileで確認したあと、Resendでメールを送る処理です。
import type { APIRoute } from "astro";
import { env as workerEnv } from "cloudflare:workers";
const env = workerEnv as unknown as Record<string, string>;
const json = (body: unknown, status = 200) =>
new Response(JSON.stringify(body), {
status,
headers: { "Content-Type": "application/json; charset=UTF-8" },
});
export const POST: APIRoute = async ({ request, clientAddress }) => {
const form = await request.formData();
const name = String(form.get("name") ?? "").trim();
const email = String(form.get("email") ?? "").trim();
const message = String(form.get("message") ?? "").trim();
if (!name || !email || !message) {
return json({ success: false, message: "すべての項目を入力してください。" }, 400);
}
// 1. Turnstileで、送信したのが人間か確認する
const verify = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
method: "POST",
body: new URLSearchParams({
secret: env.TURNSTILE_SECRET_KEY,
response: String(form.get("cf-turnstile-response") ?? ""),
remoteip: clientAddress,
}),
});
const result = (await verify.json()) as { success: boolean };
if (!result.success) {
return json({ success: false, message: "認証に失敗しました。もう一度お試しください。" }, 400);
}
// 2. Resendでメールを送る
const res = await fetch("https://api.resend.com/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${env.RESEND_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: env.CONTACT_FROM_EMAIL,
to: [env.CONTACT_ADMIN_EMAIL],
reply_to: email,
subject: `【お問い合わせ】${name}様より`,
text: `お名前:${name}\nメール:${email}\n\n${message}`,
}),
});
if (!res.ok) {
return json({ success: false, message: "送信に失敗しました。時間をおいてお試しください。" }, 500);
}
return json({ success: true });
};reply_to に訪問者のアドレスを入れているので、届いたメールにそのまま返信すれば、相手に返事ができます。
フォームのページ
次に、src/pages/contact.astro というファイルを作ります。テンプレートによってレイアウトの名前や場所が違うことがあるので、1行目の import は、ほかのページ(src/pages/index.astro など)に合わせて書き換えてください。
---
import Base from "../layouts/Base.astro";
import { env } from "cloudflare:workers";
const siteKey = (env as unknown as Record<string, string>).TURNSTILE_SITE_KEY;
---
<Base title="お問い合わせ">
<h1>お問い合わせ</h1>
<form id="contact-form">
<label>お名前 <input name="name" required /></label>
<label>メールアドレス <input name="email" type="email" required /></label>
<label>お問い合わせ内容 <textarea name="message" rows="6" required></textarea></label>
<div class="cf-turnstile" data-sitekey={siteKey}></div>
<button type="submit">送信する</button>
<p id="contact-result"></p>
</form>
<script is:inline src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<script is:inline>
const form = document.getElementById("contact-form");
const result = document.getElementById("contact-result");
form.addEventListener("submit", async (e) => {
e.preventDefault();
result.textContent = "送信中です…";
const res = await fetch("/api/contact", { method: "POST", body: new FormData(form) });
const data = await res.json();
if (data.success) {
form.reset();
result.textContent = "送信しました。お問い合わせありがとうございます。";
} else {
result.textContent = data.message;
}
if (window.turnstile) window.turnstile.reset();
});
</script>
</Base>見た目は最低限なので、サイトのデザインに合わせてCSSを追加してください。
このフォームの設置は、Claude CodeなどのAIにお願いして作ってもらうこともできます。AIとの接続方法は、【AI活用編】MCPサーバーでClaude Code・Codexからサイトを構築・更新するで解説しています。
Step 6 パソコンで試してから公開する
パソコン上(npm run dev)で試すときは、シークレットの代わりに、プロジェクトのフォルダに .dev.vars というファイルを作って値を書きます。
RESEND_API_KEY=re_ここにAPIキー
TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AATURNSTILE_SECRET_KEY に書いている値は、Cloudflareが用意しているテスト用のキーで、必ず「人間」と判定されます。パソコンで試すときは、wrangler.jsonc の TURNSTILE_SITE_KEY も、テスト用の 1x00000000000000000000AA に一時的に変えておくと確実です。本番に戻す前に、元のサイトキーに戻すのを忘れないでください。
注意:.dev.vars にはAPIキーが入っているので、Gitなどで公開しないようにしてください。EmDashのテンプレートでは、最初から .gitignore に含まれているか確認しておくと安心です。
動作を確認できたら、いつものようにビルドしてデプロイします。
npm run build
npx wrangler deploy独自ドメインの /contact を開いてフォームから送信し、CONTACT_ADMIN_EMAIL に設定したアドレスにメールが届けば完成です。
つまずきやすいポイント
Resendのサイトでボタンが表示されない
Macの場合、ChromeなどのブラウザでResendのサイトを開くと、ボタンが表示されないことがあります。Safariで開き直してみてください。
メールが届かない
次の3点を確認してください。
- Resendの「Domains」で、ドメインのステータスが「Verified」になっているか
- CONTACT_FROM_EMAIL のアドレスが、Resendに登録したドメインのものになっているか
- 受信側の迷惑メールフォルダに入っていないか
Resendの管理画面の「Emails」で、送信の記録やエラーを確認することもできます。
「認証に失敗しました」と表示される
Turnstileのホスト名に独自ドメインが登録されているか、サイトキーとシークレットキーが同じウィジェットのものか確認してください。
APIキーを間違って公開してしまった
すぐにResendの管理画面でそのキーを削除し、新しいキーを発行します。発行したキーは、もう一度 npx wrangler secret put RESEND_API_KEY で登録し直してください。
まとめ
これで、EmDashのサイトにお問い合わせフォームを設置できました。
- Resendのアカウントを作った(Macで画面がおかしいときはSafariで開く)
- Resendに独自ドメインを登録し、CloudflareのDNSに設定を追加した
- APIキーを発行し、シークレットとして登録した
- Turnstileでいたずら送信の対策をした
- フォームのページと、メールを送るプログラムを追加した
- 動作を確認して、デプロイした
まだサイトを公開していない方や、独自ドメインの設定を確認したい方は、【公開編】Cloudflareで構築して独自ドメインで公開するまでもあわせてご覧ください。

