CloudflareでStatic+Worker
Cloudflare で
Static(画面)+Worker(処理)構成を作る手順を、実務レベルでそのまま使える形で説明します。
■ 全体構成(これを作る)
ユーザー
↓
Cloudflare Pages(Static)
↓ fetch()
Cloudflare Workers(API)
↓
DB / KV(必要なら)
■ 手順①:Staticサイトを用意
例:Astro
npm create astro@latest
cd my-site
npm run build
👉 dist/ フォルダができる(これがStatic)
■ 手順②:Cloudflare Pagesにデプロイ
方法(簡単)
-
GitHubにpush
-
Cloudflareダッシュボード
-
「Pages」→「プロジェクト作成」
-
リポジトリ選択
設定
-
Build command:
npm run build -
Output directory:
dist
👉 これで
Staticサイト公開完了
■ 手順③:Worker(API)を作る
CLI準備
npm install -g wrangler
wrangler login
Worker作成
wrangler init my-worker
cd my-worker
コード編集
src/index.js
export default {
async fetch(request) {
return new Response(
JSON.stringify({ message: "Hello from Worker!" }),
{ headers: { "Content-Type": "application/json" } }
);
}
};
デプロイ
wrangler deploy
👉 URLが発行される
例:
https://my-worker.xxx.workers.dev
■ 手順④:StaticからWorkerを呼ぶ
Static側(例:AstroやHTML)
<button onclick="callApi()">API実行</button>
<script>
async function callApi() {
const res = await fetch("https://my-worker.xxx.workers.dev");
const data = await res.json();
alert(data.message);
}
</script>
👉 これで
Static → Worker通信完成
■ 手順⑤(重要):同一ドメイン化(本番向け)
そのままだと
-
Pages →
example.pages.dev -
Worker →
workers.dev
👉 ドメインが分かれる
解決方法:Workerをルーティング
Cloudflare設定:
-
Workers → 「Routes」
-
例:
example.com/api/*
👉 Workerに紐づけ
呼び出しURL
fetch("/api/hello")
👉 同一ドメインで安全&高速
■ 手順⑥(応用):フォーム送信
// Worker側
export default {
async fetch(request) {
if (request.method === "POST") {
const data = await request.json();
return new Response(
JSON.stringify({ received: data }),
{ headers: { "Content-Type": "application/json" } }
);
}
}
};
// フロント
fetch("/api/form", {
method: "POST",
body: JSON.stringify({ name: "test" })
});
■ よくある構成パターン
① シンプル
-
Pages(静的)
-
Worker(API)
② 本格
-
Pages
-
Worker
-
KV(キャッシュ)
-
D1(DB)
■ メリット
-
超高速(CDN + エッジ)
-
サーバー不要
-
スケール自動
■ よくあるミス
❌ CORSエラー
→ 同一ドメインにする
❌ Worker URL直叩き
→ /api/ ルーティングにする
❌ build設定ミス
→ dist確認
■ まとめ
-
Static=画面
-
Worker=処理
-
Cloudflareで両方簡単に統合できる
「Worker」と「Staticサイト」は、仕組みとできることが根本的に違うので、用途によって使い分けます。
■ 結論(ざっくり)
-
Staticサイト=「ただの完成済みファイル(HTMLなど)を配るだけ」
-
Worker=「サーバーの代わりにプログラムを動かせる」
■ Staticサイトとは
✔ 特徴
-
HTML / CSS / JSファイルをそのまま配信
-
サーバー処理なし
-
超高速・安定・安い
✔ できること
-
ホームページ表示
-
ブログ(事前生成)
-
LP(ランディングページ)
✔ できないこと
-
ログイン処理
-
DB操作
-
API処理
👉 例
-
Astroで生成したサイト
-
Webstudioの書き出しHTML
-
GitHub Pages / Cloudflare Pages
■ Workerとは
(例:Cloudflare Workers)
✔ 特徴
-
JavaScriptでサーバー処理を書ける
-
エッジ(世界中)で実行される
-
APIや動的処理が可能
✔ できること
-
API作成
-
認証処理
-
HTMLを書き換え
-
DB(KV / D1など)操作
✔ 例
export default {
async fetch(request) {
return new Response("Hello from Worker!");
}
}
■ 一番大事な違い(本質)
| 項目 | Staticサイト | Worker |
|---|---|---|
| 中身 | ファイル | プログラム |
| 処理 | なし | あり |
| 動的処理 | ❌ | ✅ |
| 表示速度 | 超速い | 速い(+処理) |
| 用途 | 表示専用 | ロジック |
■ よくある構成(実務)
実際は「組み合わせ」が最強👇
-
Staticサイト(UI)
-
Worker(API・処理)
👉 例
-
フロント:Static(Astro)
-
バック:Worker(API)
■ イメージ
-
Static = 「印刷された本」
-
Worker = 「受付スタッフ」
■ どっちを使うべき?
-
サイト表示だけ → Static
-
何か処理したい(ログイン・送信) → Worker
-
迷ったら → Static + Worker
Cloudflare で
Staticサイト → Worker → メール送信の実装を、コピペで動くレベルで説明します。
■ 全体構成
HTMLフォーム(Static)
↓ POST
Worker(API)
↓
メール送信サービス(Resendなど)
■ ① メール送信サービス準備
ここではシンプルな
Resend を使います
手順
-
Resendに登録
-
APIキー取得
例:
re_xxxxxxxxx
■ ② Workerに環境変数を設定
wrangler secret put RESEND_API_KEY
👉 APIキーを安全に保存
■ ③ Workerコード(メール送信)
src/index.js
export default {
async fetch(request, env) {
if (request.method !== "POST") {
return new Response("Method Not Allowed", { status: 405 });
}
const data = await request.json();
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: "onboarding@resend.dev",
to: "あなたのメール@example.com",
subject: "お問い合わせが届きました",
html: `
<h1>お問い合わせ内容</h1>
<p>名前: ${data.name}</p>
<p>メール: ${data.email}</p>
<p>内容: ${data.message}</p>
`
})
});
return new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" }
});
}
};
■ ④ デプロイ
wrangler deploy
■ ⑤ Static側フォーム
<form id="contact-form">
<input type="text" name="name" placeholder="名前" required>
<input type="email" name="email" placeholder="メール" required>
<textarea name="message" placeholder="内容"></textarea>
<button type="submit">送信</button>
</form>
<script>
document.getElementById("contact-form").addEventListener("submit", async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = {
name: formData.get("name"),
email: formData.get("email"),
message: formData.get("message")
};
const res = await fetch("/api/contact", {
method: "POST",
body: JSON.stringify(data)
});
alert("送信完了");
});
</script>
■ ⑥ ルーティング(重要)
Cloudflareで設定👇
example.com/api/*
→ Workerに接続
👉 これで
/api/contact
がWorkerになる
■ 完成イメージ
-
/→ Staticページ -
/api/contact→ Worker(メール送信)
■ よくあるエラー
❌ メールが届かない
- Resendの送信元ドメイン未認証
👉 対策
- 自分のドメインをResendに追加
❌ CORSエラー
👉 同一ドメインにする(/api)
❌ スパム対策なし
👉 最低限これ入れる👇
if (!data.email.includes("@")) {
return new Response("Invalid", { status: 400 });
}
■ 応用(おすすめ)
✔ 自動返信(ユーザーにも送る)
to: data.email
✔ スパム対策
- Cloudflare Turnstile(reCAPTCHA代替)
✔ 保存もしたい
-
D1(DB)
-
KV(軽量保存)
■ まとめ
-
Staticだけではメール送信できない
-
Workerを挟めば可能
-
Resendで超簡単に実装できる
Webstudio に
フォーム+Workerメール送信(Resend)を組み込む方法を、実際に動く形でまとめます。
■ 全体像
Webstudio(フォームUI)
↓ fetch
Cloudflare Worker(API)
↓
Resend(メール送信)
■ ① 前提(先にやること)
すでに作っておく👇
-
Cloudflare Worker(/api/contact)
-
Resend APIキー設定済み
👉 ここは前の手順どおり
■ ② Webstudioでフォーム作成
手順
-
フォーム要素追加
-
中に入力フィールド配置
構成例
-
Input(name=“name”)
-
Input(name=“email”)
-
Textarea(name=“message”)
-
Button(送信)
👉 name属性が重要
■ ③ デフォルト送信を無効化
Webstudioのフォームは通常「外部送信」するので止める
方法
-
Formの「Action」 → 空にする
-
Method → POST(でも実際はJSで制御)
■ ④ カスタムJSを追加(これが核心)
Webstudioの
Custom Code(Before ) に追加👇
<script>
document.addEventListener("DOMContentLoaded", () => {
const form = document.querySelector("form");
form.addEventListener("submit", async (e) => {
e.preventDefault();
const formData = new FormData(form);
const data = {
name: formData.get("name"),
email: formData.get("email"),
message: formData.get("message")
};
try {
const res = await fetch("/api/contact", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
if (res.ok) {
alert("送信しました");
form.reset();
} else {
alert("送信エラー");
}
} catch (err) {
alert("通信エラー");
}
});
});
</script>
■ ⑤ Cloudflare側ルーティング
example.com/api/*
→ Worker
👉 これ必須
■ ⑥ デプロイ
Webstudio → Publish
(Cloudflare Pages or export)
■ 動作確認
-
フォーム入力
-
送信
-
メール届く
■ よくある詰まりポイント
❌ 送信されない
- form selector間違い
👉querySelector("form")を確認
❌ データが空
👉 name属性ミス
<input name="email">
❌ CORSエラー
👉 必ず /api/contact にする
(Worker直URLは使わない)
■ UX改善(おすすめ)
✔ 送信中表示
button.textContent = "送信中...";
✔ 成功メッセージ表示
alert → 非表示divに変更
■ 応用(強くおすすめ)
① スパム対策
👉 Cloudflare Turnstile
② バリデーション強化
if (!data.email.includes("@")) return;
③ 自動返信メール
Workerで追加
■ まとめ
-
Webstudioは「見た目担当」
-
Workerは「処理担当」
-
JSでつなぐだけで完成
Cloudflare の
Turnstile(スパム対策)+フォーム送信+メール送信(Resend)完全版を
👉 そのままコピペで動く形でまとめます。
■ 全体構成
Webstudio(フォーム)
↓ + Turnstileトークン
Worker(検証 + メール送信)
↓
Resend(メール)
■ ① Turnstileキー取得
Cloudflareダッシュボード
→ Turnstile → サイト追加
取得するもの👇
SITE_KEY(公開用)
SECRET_KEY(Worker用)
■ ② Workerにシークレット登録
wrangler secret put TURNSTILE_SECRET
wrangler secret put RESEND_API_KEY
■ ③ Workerコード(完全版)
src/index.js
export default {
async fetch(request, env) {
if (request.method !== "POST") {
return new Response("Method Not Allowed", { status: 405 });
}
const data = await request.json();
// ------------------------
// ① Turnstile検証
// ------------------------
const token = data.token;
const verifyRes = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: `secret=${env.TURNSTILE_SECRET}&response=${token}`
});
const verifyData = await verifyRes.json();
if (!verifyData.success) {
return new Response(JSON.stringify({ error: "bot detected" }), {
status: 400
});
}
// ------------------------
// ② バリデーション
// ------------------------
if (!data.email || !data.email.includes("@")) {
return new Response("Invalid email", { status: 400 });
}
// ------------------------
// ③ メール送信(Resend)
// ------------------------
await fetch("https://api.resend.com/emails", {
method: "POST",
headers: {
"Authorization": `Bearer ${env.RESEND_API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
from: "onboarding@resend.dev",
to: "あなたのメール@example.com",
subject: "お問い合わせ",
html: `
<h2>新しい問い合わせ</h2>
<p>名前: ${data.name}</p>
<p>メール: ${data.email}</p>
<p>内容: ${data.message}</p>
`
})
});
return new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" }
});
}
};
■ ④ Webstudio側(HTML)
フォームにTurnstile追加👇
<form id="contact-form">
<input name="name" placeholder="名前" required>
<input name="email" type="email" placeholder="メール" required>
<textarea name="message" placeholder="内容"></textarea>
<!-- Turnstile -->
<div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div>
<button type="submit">送信</button>
</form>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
■ ⑤ JS(超重要)
<script>
document.addEventListener("DOMContentLoaded", () => {
const form = document.getElementById("contact-form");
form.addEventListener("submit", async (e) => {
e.preventDefault();
const formData = new FormData(form);
const token = document.querySelector("[name='cf-turnstile-response']").value;
const data = {
name: formData.get("name"),
email: formData.get("email"),
message: formData.get("message"),
token: token
};
const btn = form.querySelector("button");
btn.disabled = true;
btn.textContent = "送信中...";
try {
const res = await fetch("/api/contact", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
if (res.ok) {
alert("送信成功");
form.reset();
} else {
alert("送信失敗");
}
} catch (err) {
alert("通信エラー");
}
btn.disabled = false;
btn.textContent = "送信";
});
});
</script>
■ ⑥ ルーティング設定(必須)
example.com/api/*
→ Worker
■ 完成
これで👇
-
ボット → ブロック(Turnstile)
-
正常ユーザー → メール送信
■ よくあるミス
❌ tokenが取れない
👉 cf-turnstile-response のname確認
❌ always失敗
👉 SECRETキー間違い
❌ メール届かない
👉 Resend ドメイン未認証
■ 強化(おすすめ)
✔ 自動返信
to: data.email
✔ IP制限
Workerで可能
✔ 保存(D1)
問い合わせ履歴DB化
■ まとめ
-
Turnstileでスパムほぼ防止
-
Workerで安全に処理
-
WebstudioはUIだけでOK
必要なら👇
👉「Webstudioの画面操作レベルでの配置手順」
👉「D1保存付き完全版」
👉「reCAPTCHAとの違い」
も出せます。