Cloudflare の EmDash でブログを立ち上げ、日本語のテックブログ向けにカスタマイズして公開するまで
WordPress の後継をうたう Cloudflare 製 CMS「EmDash」を WSL 上で動かし、Blog テンプレートを日本語のテックブログ向けに整えて、Cloudflare Workers で公開するまでの記録です。
Cloudflare が発表したオープンソースの CMS「EmDash」で、このブログを立ち上げました。この記事では、WSL 上のローカル環境で動かし、日本語のテックブログ向けに見た目を整えて、Cloudflare で公開するまでの手順をまとめます。
EmDash とは
EmDash は Cloudflare が作った TypeScript 製の CMS で、「WordPress の精神的後継」をうたっています。主な特徴は次のとおりです。
- Astro ベース:テンプレートは Astro で書かれていて、ページはすべてサーバーで描画されます
- プラグインのサンドボックス化:WordPress で問題になりがちなプラグインの脆弱性に対して、プラグインを隔離した環境で動かすことで対処しています
- AI ネイティブ:MCP サーバーや CLI が最初から用意されていて、AI エージェントから記事を操作できます(この記事も CLI 経由で投稿しています)
- 動かす場所を選べる:Cloudflare(Workers + D1 + R2)でも、Node.js + SQLite でも動きます
環境の準備
作業環境は Windows の WSL2(Linux)です。EmDash には Node.js 22.23.4 以上が必要ですが、Node.js が入っていなかったので nvm でインストールしました。
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
nvm install 24
node -v # v24.22.2プロジェクトの作成
create-emdash には対話形式のプロンプトがありますが、オプションを付けると一度に作成できます。今回はブログ用の blog テンプレートを使い、まずはローカルで試すために Node.js 向けの構成を選びました。
npm create emdash@latest my-blog -- --template blog --platform node --pm npm --install --yes
cd my-blog
npm run devBlog テンプレートには、カテゴリ・タグ、全文検索、RSS、ダークモードが最初から入っています。データベースはプロジェクト直下の SQLite ファイル(data.db)です。
初期設定
開発サーバーを起動したら、http://localhost:4321/_emdash/admin/ を開きます。初回はセットアップ画面が表示されます。
- サイトタイトルを入力する(後から Settings → General で変更できます)
- メールアドレスを入力する
- パスキーを登録する
ログインはパスワードではなくパスキーで行います。パスキーは登録したホスト名でしか使えないので、本番環境では公開先のドメインでもう一度初期設定をする必要があります。
日本語のテックブログ向けにカスタマイズ
テンプレートは英語圏向けのデザインなので、次の3点を変更しました。
1. 日本語フォントの追加
テンプレートの標準フォントは Inter(本文)と JetBrains Mono(コード)で、日本語のフォントは指定されていません。そこで Astro のフォント機能で Noto Sans JP も読み込み、英字は Inter、日本語は Noto Sans JP で表示されるようにしました。
// astro.config.mjs(抜粋)
fonts: [
{ provider: fontProviders.google(), name: "Inter", cssVariable: "--font-latin", /* ... */ },
{ provider: fontProviders.google(), name: "JetBrains Mono", cssVariable: "--font-code", /* ... */ },
{ provider: fontProviders.google(), name: "Noto Sans JP", cssVariable: "--font-jp", weights: [400, 500, 700] },
],/* src/styles/theme.css */
:root {
--font-body: var(--font-latin), var(--font-jp), system-ui, sans-serif;
--font-mono: var(--font-code), var(--font-jp), ui-monospace, monospace;
}あわせて、<html lang="ja"> にし、日付を「2026年11月8日」の形式で表示するようにしました。行間も日本語の文章に合わせて広げ、禁則処理を有効にしています。
2. テックブログ風のデザイン
EmDash のテンプレートは、色や角丸などのデザイントークンが src/styles/tokens.css にまとまっています。変更したい値を src/styles/theme.css で上書きするだけで、サイト全体のデザインを変えられます。
:root {
/* 差し色: 藍色(ライト / ダーク) */
--color-brand: light-dark(#1e3a8a, #93b4ff);
--color-brand-hover: light-dark(#172e6e, #b7cdff);
/* 角丸を小さめにしてシャープに */
--radius-lg: 8px;
--radius-xl: 12px;
}このほか、次の変更をしました。
- 背景をわずかに青みのあるグレーにした
- 本文中のリンクとインラインコード(
like this)に差し色を付けた - 見出しの左に藍色の線を付けた
- コードブロックをエディタ風の濃い背景にした
3. 表示文言の日本語化
「Latest posts」「On this page」「min read」など、テンプレートに直接書かれている英語を「新着記事」「目次」「約◯分で読めます」のように置き換えました。対象は src/pages/ と src/components/ 以下の Astro ファイルです。
Cloudflare にデプロイする
ローカルで形になったので、Cloudflare Workers に公開しました。データベースは D1、画像などの保存先は R2 を使います。
Node.js 構成から Cloudflare 構成に切り替える
最初は Node.js + SQLite の構成で作っていたので、Cloudflare 向けの構成に切り替えました。--platform cloudflare で作った Blog テンプレートと見比べると、src/ 以下は src/worker.ts があるかどうかの違いだけでした。そのため、設定ファイルを差し替えるだけで済みました。
// astro.config.mjs(抜粋)
import cloudflare from "@astrojs/cloudflare";
import { d1, r2 } from "@emdash-cms/cloudflare";
export default defineConfig({
output: "server",
adapter: cloudflare(),
integrations: [
react(),
emdash({
database: d1({ binding: "DB", session: "auto", coalesce: true }),
storage: r2({ binding: "MEDIA" }),
}),
],
// fonts の設定はそのまま
});あわせて、wrangler.jsonc と src/worker.ts を追加し、package.json の依存関係を Cloudflare 用のもの(@astrojs/cloudflare、@emdash-cms/cloudflare、wrangler)に入れ替えました。
なお、npm 11 では依存パッケージのインストールスクリプトが標準で止められます。wrangler の動作には esbuild と workerd のスクリプトが必要なので、許可してから入れ直しました。
npm install-scripts approve esbuild workerd
npm rebuild esbuild workerdデプロイ
wrangler で Cloudflare にログインしてからデプロイします。D1 データベースや R2 バケットは、初回デプロイ時に wrangler が自動で作ってくれます。
npx wrangler login
npm run deploy # astro build && wrangler deployただ、Cloudflare を使い始めたばかりのアカウントだったので、すんなりとはいきませんでした。つまずいたのは次の3点です。
- workers.dev のサブドメインがない:wrangler が自動で登録しようとしたサブドメインはほかの人が使っていたため、ダッシュボードで登録しました
- R2 が有効になっていない:ダッシュボードで R2 を有効にする必要があります。無料枠で使えますが、支払い方法の登録が必要です
- KV の二重作成エラー:最初のデプロイが途中で失敗したとき、セッション用の KV namespace だけ作られていました。再デプロイで同じ名前の KV を作ろうとしてエラーになったので、
wrangler.jsoncに作成済みの ID を書きました
"kv_namespaces": [
{ "binding": "SESSION", "id": "<作成済みの KV namespace の ID>" }
],wrangler deploy は、Astro がビルド時に生成した設定を読みます。そのため、wrangler.jsonc を変えたあとは npm run deploy でビルドからやり直す必要があります。
マイグレーションが途中で止まった
デプロイ後に管理画面を開くと、「Database migrations are required.」と表示されました。
EmDash は最初のリクエストでデータベースのマイグレーションを自動で実行します。ところが、全91件のうち 050 まで進んだところで止まっていました。Workers の1リクエストあたりの実行時間の上限に引っかかったのだと思います。
そこで、ビルド時に生成される .emdash/migrations.json を使い、手元の CLI から残りのマイグレーションを適用しました。D1 を操作するには API トークンが必要なので、wrangler でログインしたときのトークンを使っています。
export CLOUDFLARE_ACCOUNT_ID=<アカウント ID>
export CLOUDFLARE_API_TOKEN="$(npx wrangler auth token)"
# 状態を確認する(途中で止まった処理のロックが残っていることも分かる)
npx emdash migrate --manifest .emdash/migrations.json --check --d1 <D1 の ID>
# ロックを引き継いで、残りを適用する
npx emdash migrate --manifest .emdash/migrations.json --d1 <D1 の ID> \
--expected-target-fingerprint <check で表示された値> \
--take-over-lock <check で表示されたロック ID>本番の初期設定
マイグレーションが終わったら、公開した URL の /_emdash/admin/ を開いて初期設定をします。初期設定が終わるまでは、最初にアクセスした人が管理者になってしまいます。デプロイしたらすぐに済ませましょう。パスキーはホスト名ごとに登録するので、ローカルで登録したものとは別に、本番用を登録します。
記事の移行と暗号化キー
記事は CLI で本番に投稿しました。emdash login を実行すると、ブラウザでコードを入力して承認する流れでログインできます。
npx emdash login --url https://tech-blog.yu-maeda.workers.dev
npx emdash content create posts --url https://tech-blog.yu-maeda.workers.dev \
--file post.json --slug emdash-blog-setup最後に、EmDash がプラグインの秘密情報などを暗号化するときに使うキーを生成し、Worker のシークレットに登録しました。このキーをなくすと暗号化した情報を復元できなくなるので、控えを安全な場所に保管しておきます。
npx emdash secrets generate
npx wrangler secret put EMDASH_ENCRYPTION_KEYこれで、このブログが https://tech-blog.yu-maeda.workers.dev/ で公開されました。
やってみた感想
create-emdashのおかげで、作成から管理画面に入るまでが数分で終わる- デザインの変更はほぼ
theme.cssの上書きだけで済むので、テーマを作るときの見通しがよい - パスキーでのログインは手軽で、パスワードを管理しなくてよいのが楽
- 一方で、テンプレートの文言は多言語対応されていないので、日本語にするにはファイルを直接書き換える必要がある
- Cloudflare へのデプロイは、アカウントの初期準備(サブドメインと R2)と、初回のマイグレーションが止まる問題で少し苦労した。手順として覚えておけば、次からはスムーズにいきそう
次にやること
- 独自ドメインを設定する
- カテゴリやタグを整理して、記事を増やしていく
コメント
No comments yet