Cloudflare Pages/WorkersでSvelteKitアプリを運用する

Alcogyでは受託開発・OEM提供のいずれにおいても、SvelteKitアプリのホスティング先としてCloudflareを標準にしています。ここではCloudflare Pages/Workersでの運用構成と、実際にハマったポイントを共有します。

adapter-cloudflareでビルドする

SvelteKitでCloudflareにデプロイするには@sveltejs/adapter-cloudflareを使います。svelte.config.jsでアダプターを指定し、D1やKVといったバインディングをwrangler.jsonc側で定義します。

// wrangler.jsonc
{
  "name": "example-app",
  "compatibility_date": "2026-05-01",
  "compatibility_flags": ["nodejs_compat"],
  "d1_databases": [
    { "binding": "DB", "database_name": "example-db", "database_id": "xxxx" }
  ],
  "kv_namespaces": [
    { "binding": "SESSION_KV", "id": "yyyy" }
  ],
  "r2_buckets": [
    { "binding": "ASSETS_BUCKET", "bucket_name": "example-assets" }
  ]
}

バインディングはevent.platform.env経由でアクセスできるため、+server.ts+page.server.tsの中でDBやKVを直接呼び出せます。

// src/routes/api/orders/+server.ts
import type { RequestHandler } from './$types';

export const GET: RequestHandler = async ({ platform }) => {
  const db = platform?.env.DB;
  const { results } = await db!.prepare('SELECT * FROM orders LIMIT 20').all();
  return new Response(JSON.stringify(results), {
    headers: { 'content-type': 'application/json' },
  });
};

環境変数とシークレットは明確に分離する

Cloudflareでは環境変数(vars)とシークレット(secrets)の扱いが異なります。wrangler.jsoncvarsに書いた値はビルド成果物やダッシュボードから見えるため、APIキーのような機密情報は必ずwrangler secret putで登録します。

# 本番環境にシークレットを登録
bunx wrangler secret put ANTHROPIC_API_KEY

# ローカル開発用は.dev.varsに記述(gitignore対象)
echo 'ANTHROPIC_API_KEY=sk-xxxx' >> .dev.vars

Alcogyでは.dev.varsをローカル専用にし、ステージング・本番のシークレットはCloudflareダッシュボードの管理画面から直接登録しています。シークレットは一度設定すれば頻繁に変更するものではなく、逆にCI経由で登録する構成にすると、GitHub側にシークレットの値を一時的にでも持たせることになり、漏えい経路を増やすだけだと考えています。デプロイのたびに値を打ち直す必要がないという意味でも、ダッシュボードから一度登録して終わりにするほうがシンプルです。

ハマりやすいポイント

  • Node.js APIの互換性: nodejs_compatフラグを付けていても、一部のNode組み込みモジュールは動作しません。cryptobufferは概ね問題ありませんが、ファイルシステム操作に依存するライブラリは避ける、あるいはWorkers向けの代替実装に置き換える必要があります。
  • プレビューデプロイのバインディング漏れ: PagesのプレビューデプロイはブランチごとにURLが発行されますが、wrangler.jsoncのバインディング設定を本番用のIDのままにしていると、プレビュー環境が本番DBを参照してしまいます。環境ごとにenv.previewのようなセクションを分けて定義するようにしています。
  • キャッシュとSSRの干渉: Cloudflareのエッジキャッシュは強力ですが、SvelteKitのSSRページに対して意図せずキャッシュが効いてログイン状態のページが他ユーザーに配信される、という事故が起きえます。Cache-Control: privateを明示するか、Cookieを含むレスポンスはキャッシュ対象から除外するルールをPages側に設定しています。

デプロイフローの型

AlcogyではCloudflareのダッシュボードからGitHubリポジトリを直接連携し、Cloudflare側のGit連携機能に本番デプロイを任せる構成を基本にしています。mainへのプッシュを検知して自動でビルド・デプロイが走るため、GitHub Actionsのワークフローを自前で書く必要がありません。ブランチ・プルリクエスト単位のプレビューデプロイも同じ仕組みで自動生成されるため、レビュー段階で実際の動作を確認できます。SvelteKit+Cloudflareの組み合わせは、初期構築のコストが低い割に、ここまで紹介したような細部の設定を丁寧に詰めることで、業務システムの本番運用にも十分耐えるインフラになると感じています。