Cloudflare Workersは、EmDash向けの高速でグローバルに分散されたランタイムを提供します。このガイドでは、データベースにD1、メディアストレージにR2を使用したデプロイについて説明します。
前提条件
- Cloudflareアカウント
- Wrangler CLIがインストール済み(
npm install -g wrangler) - Cloudflareで認証済み(
wrangler login)
バインディングの設定
プロジェクトルートにD1とR2のバインディングを含む wrangler.jsonc を作成します。Wranglerは、まだ存在しない場合、最初のデプロイ時に両方のリソースをプロビジョニングします。
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-emdash-site",
"compatibility_date": "2025-01-15",
"compatibility_flags": ["nodejs_compat"],
"d1_databases": [
{
"binding": "DB",
"database_name": "emdash-db",
},
],
"r2_buckets": [
{
"binding": "MEDIA",
"bucket_name": "emdash-media",
},
],
}
EmDashの設定
Astro設定をD1とR2を使用するように更新します:
import { defineConfig } from "astro/config";
import cloudflare from "@astrojs/cloudflare";
import react from "@astrojs/react";
import emdash from "emdash/astro";
import { d1, r2 } from "@emdash-cms/cloudflare";
export default defineConfig({
output: "server",
adapter: cloudflare(),
integrations: [
react(), // 必須 — 管理UIはReactアプリ
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
}),
],
});
初回起動
データベースマイグレーションはデプロイ後の最初のリクエスト時に自動的に実行され、新しく適用するものがあれば以降の起動時にも実行されます。
データベースが空(コレクションなし)でセットアップウィザードが完了していない場合、EmDashは初回起動時にシードファイルも適用します。シードはビルド時に .emdash/seed.json、package.json#emdash.seed のパス、または seed/seed.json から読み取られ — 最初に見つかったものが使用され — バンドルにインライン化されます。いずれも存在しない場合、組み込みのデフォルトシードが使用されます。既存のデータベースへの後続のデプロイはコンテンツをそのまま残します。
すでにデプロイされたサイトのスキーマやコンテンツモデルを変更するには、デプロイされたサイトの進化を参照してください。
スケジュール公開
Cloudflare Workersでは、スケジュール公開、プラグインcron、メンテナンスタスクはWorker Cron Triggerから実行されます。新しいCloudflareテンプレートにはこのセットアップが自動的に含まれています。既存のプロジェクトを更新する場合は、@emdash-cms/cloudflare/worker からEmDash Workerエントリをエクスポートします:
export { default, PluginBridge } from "@emdash-cms/cloudflare/worker";
次に wrangler.jsonc にCron Triggerを追加します:
{
"triggers": {
"crons": ["* * * * *"],
},
}
デプロイ
Cloudflare Workersにデプロイ:
wrangler deploy
サイトは https://my-emdash-site.<your-subdomain>.workers.dev でライブになりました。
Read Replica
グローバルに分散されたサイトの場合、D1のリードレプリケーションを有効にして、常にプライマリデータベースにアクセスするのではなく、近くのレプリカに読み取りクエリをルーティングします。これにより、プライマリリージョンから離れた訪問者のレイテンシーが大幅に削減されます。
emdash({
database: d1({
binding: "DB",
session: "auto",
}),
storage: r2({ binding: "MEDIA" }),
}),
Cloudflareダッシュボードまたは REST APIで、D1データベース自体のリードレプリケーションも有効にする必要があります。
セッションモードとブックマークベースの整合性の仕組みについては、データベースオプション — Read Replicaを参照してください。
Object Cache
D1の読み取り負荷を軽減するには、コンテンツと設定のクエリ結果をCloudflare KVにキャッシュします。リクエストごとにデータベースをクエリする代わりに、KVから読み取りが提供されます:
import { d1, r2, kvCache } from "@emdash-cms/cloudflare";
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
objectCache: kvCache({ binding: "CACHE" }),
}),
KVのセットアップ、オプション、無効化の動作については、Object Cacheを参照してください。
Workers Cache
CloudflareのWorkers Cache(wrangler.jsonc の "cache": { "enabled": true })は、Worker の前に エッジキャッシュを配置します:一致するリクエストはWorkerを実行せずに提供されます。これはEmDashとうまく機能します:
- EmDashの管理とAPIレスポンスは
Cache-Control: private, no-storeを送信し、保存されることはありません。 - パブリックページは、返す
Cache-Controlヘッダーを通じて独自のキャッシュを制御します。
有効化前に知っておくべき2つのこと:
Cache-Controlヘッダーのないレスポンスもキャッシュされます。 Workers CacheはRFC 9111のヒューリスティックフレッシュネスを適用します — ヘッダーなしの200は2時間キャッシュされます。すべてのカスタムルートに明示的なCache-Controlを指定してください(セッション依存のものにはprivate, no-storeを使用)。- キャッシュされたページはログイン中のエディターと共有されます。 キャッシュはWorkerの前に実行されるため、リクエストCookieに基づいてバイパスできません。ログイン中のエディターは、エントリが期限切れになるまで、パブリックページのキャッシュされた匿名バリアント — ビジュアル編集ツールバーなし — を受け取る可能性があります。エディターがレンダリングしたレスポンス自体は保存されることはなく(
private, no-storeを持つ)、逆方向に漏洩することはありません。
カスタムドメイン
Cloudflareダッシュボードでカスタムドメインを追加します:
- Workers & Pages > あなたのworkerに移動
- Custom Domains > Add Custom Domain をクリック
- ドメインを入力し、DNSセットアップの手順に従います
パブリックR2アクセス
R2からメディアを直接配信するには(パフォーマンスのため推奨):
- Cloudflareダッシュボードで R2 > あなたのバケットに移動
- Settings > Public access をクリック
- パブリックアクセスを有効にし、パブリックURLをメモ
- ストレージ設定を更新:
storage: r2({
binding: "MEDIA",
publicUrl: "https://pub-xxx.r2.dev"
}),
Cloudflare Access認証
組織がCloudflare Accessを使用している場合、パスキーの代わりに認証プロバイダーとして使用でき、既存のIDプロバイダーを通じてシングルサインオンを提供します。以下の設定で有効にします:
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
roleMapping: {
"Admins": 50,
"Editors": 40,
},
}),
}),
すべての設定オプションについては、認証ガイドを参照してください。
メール
Workersでは、唯一の組み込み email:deliver ハンドラーは開発コンソールのスタブであるため、
メール依存のフロー — マジックリンクログイン、チーム招待、コメント
通知 — は本番環境で “Email is not configured” で失敗します。
cloudflareEmail() プラグインは、外部APIキーなしで、ネイティブの send_email Workerバインディングを使用して
Cloudflare Email Sending
経由で実際のメールを配信します。
1. 送信者ドメインの登録
Cloudflareダッシュボードで Email に移動し、送信元のドメイン(またはアドレス)を 確認します。Email Sendingは未確認の送信者からのメッセージを拒否します。
2. バインディングの追加
wrangler.jsonc で send_email バインディングを宣言します:
{
"send_email": [{ "name": "EMAIL" }],
}
3. プロバイダーの登録
emdash() インテグレーションにプラグインを追加します:
import { d1, r2 } from "@emdash-cms/cloudflare";
import { cloudflareEmail } from "@emdash-cms/cloudflare/plugins";
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
plugins: [
cloudflareEmail({
from: { email: "[email protected]", name: "My Site CMS" },
replyTo: "[email protected]", // オプション
binding: "EMAIL", // オプション、デフォルトは "EMAIL"
}),
],
}),
4. 有効化と選択
デプロイし、Admin → Extensions でプラグインを有効化し、 Settings → Email でプロバイダーとして選択します。
オプション
| オプション | 型 | デフォルト | 説明 |
|---|---|---|---|
from | string | { email, name? } | — (必須) | Email Sending用に登録されたドメインの送信者アドレス。 |
replyTo | string | — | オプションのReply-To。from がno-replyサブドメインアドレスの場合に便利。 |
binding | string | "EMAIL" | wrangler.jsonc の send_email バインディング名。 |
環境変数
推奨:暗号化キー
EMDASH_ENCRYPTION_KEY は、プラグインシークレットを保存時に暗号化するための
キーです(webhookトークン、Turnstileキーなど)。キーは起動時に検証され、
プラグインシークレットの暗号化は有効化されると使用されます。すべてのデプロイで設定して、
後の設定変更なしにシークレットが保護されるようにしてください。
キーはあなたが提供し、データベースには保存されません。暗号化された 暗号文のみが保存されます。キーを失うと、それで暗号化されたすべてのシークレットを失います。
以下のコマンドでキーを生成し、Workerシークレットとして保存します:
npx emdash secrets generate
wrangler secret put EMDASH_ENCRYPTION_KEY
オプション:安定値のオーバーライド
EmDashはプレビューHMACシークレットとコメンターIPハッシュソルトを 自動生成し、初回使用時にデータベースに保存します。以下の環境変数は、 値を自分で固定する必要がある場合のオーバーライドです — 例えば、別プロセスのプレビューWorkerがメインサイトとシークレットを共有する必要がある場合。
| 変数 | 目的 |
|---|---|
EMDASH_PREVIEW_SECRET | 自動生成されたプレビューHMACシークレットのオーバーライド。 |
EMDASH_IP_SALT | 自動生成されたコメンターIPハッシュソルトのオーバーライド。 |
EMDASH_AUTH_SECRET | オプション。設定されると、IPソルトソースとして使用されます(EMDASH_IP_SALT も設定されている場合はそちらが優先)。すでにこれに依存しているインストールのコメンターIPハッシュを安定に保ちます。新しいデプロイでは未設定のままにしてください。 |
設定内の環境変数には import.meta.env またはCloudflareの env バインディングでアクセスします。
EmDashが使用するすべてのシークレットの完全なインベントリ — ストレージの場所、ローテーション手順、キーを失った場合に何が壊れるかを含む — については、シークレットとキー管理を参照してください。
プレビューデプロイメント
プレビューブランチをデプロイ:
wrangler deploy --env preview
wrangler.jsonc に環境セクションを追加:
{
"env": {
"preview": {
"d1_databases": [
{
"binding": "DB",
"database_name": "emdash-db-preview",
},
],
},
},
}
トラブルシューティング
”D1 binding not found”
wrangler.jsonc のバインディング名がデータベース設定と一致していることを確認します:
// 一致する必要があります: d1({ binding: "DB" })
"binding": "DB"
“R2 binding not found”
R2バケットが正しくバインドされていることを確認します:
// 一致する必要があります: r2({ binding: "MEDIA" })
"binding": "MEDIA"
マイグレーションエラー
スキーマエラーが表示される場合は、Workerログをtail(wrangler tail)してエラーを再現し、根本的なメッセージをキャプチャしてから、その出力でissueを作成してください。