kt-tech.blog

画像

【設定・環境構築】OpenNext でNext.js SSGサイトをCloudflare Workersにデプロイする完全ガイド

Share
📌
概要: @opennextjs/cloudflareを使ってNext.jsのSSGサイトをCloudflare Workersにデプロイする方法を解説します。セットアップからSSG対応、CI/CD構築まで、本番運用に必要な設定を網羅しています。

[toc]

はじめに

  • Cloudflare PagesでNext.jsサイトを運用していたが、Edge Runtime制約が厳しくなった

  • microCMSからNotion APIへの移行に伴い、Node.jsランタイムが必要になった

  • @cloudflare/next-on-pagesはEdge Runtime必須で制約が多い

  • OpenNext(@opennextjs/cloudflare)を使えばNode.js互換でCloudflare Workers上でNext.jsが動く

この記事でわかること

  • OpenNextのセットアップ手順

  • SSG(Static Site Generation)対応の設定

  • wrangler.toml の詳細設定

  • GitHub ActionsによるCI/CD構築

  • Workers有料プランが必要な理由

対象読者

  • Cloudflare WorkersでNext.jsを動かしたい方

  • Cloudflare PagesのEdge Runtime制約に困っている方

  • Next.jsのSSGサイトをCloudflareにデプロイしたい方

1. なぜOpenNextか

@cloudflare/next-on-pages の制約

@cloudflare/next-on-pagesはCloudflare Pages向けのアダプターだが、以下の制約がある:

  • Edge Runtime必須: すべてのルートがexport const runtime = 'edge'を要求される

  • Node.js APIが使えない: crypto, fs, path等のNode.jsモジュールが利用不可

  • npm パッケージの制約: Node.js APIに依存するパッケージ(@notionhq/client, cheerio等)が動かない

OpenNextのアプローチ

@opennextjs/cloudflareはCloudflare WorkersのNode.js互換モードを活用し、Next.jsをほぼそのまま動かす。

  • Node.js APIが使える(crypto, Buffer等)

  • npm パッケージの互換性が高い

  • ISR / SSG / SSR すべてサポート

2. セットアップ

インストール

npm install @opennextjs/cloudflare
npm install -D wrangler
# esbuildの明示的インストールが必要
npm install -D esbuild

open-next.config.ts

import { defineCloudflareConfig } from '@opennextjs/cloudflare';

export default defineCloudflareConfig({});

wrangler.toml

#:schema node_modules/wrangler/config-schema.json
name = "kt-tech-blog"
main = ".open-next/worker.js"
compatibility_date = "2025-03-14"
compatibility_flags = ["nodejs_compat"]

[assets]
directory = ".open-next/assets"
binding = "ASSETS"

# KVはISRキャッシュに使用
[[kv_namespaces]]
binding = "NEXT_CACHE_WORKERS_KV"
id = "your-kv-namespace-id"

package.json のスクリプト更新

{
  "scripts": {
    "build": "opennextjs-cloudflare",
    "dev": "next dev",
    "deploy": "opennextjs-cloudflare && wrangler deploy",
    "preview": "opennextjs-cloudflare && wrangler dev"
  }
}

3. SSG対応

generateStaticParamsで全ページパスを生成

SSGでは、ビルド時に全ページのパスを静的に生成する必要がある。

// src/app/blogs/[blogId]/page.tsx
export async function generateStaticParams() {
  const allBlogs = await getAllBlogs(); // Notion APIから全記事取得
  return allBlogs.map((blog) => ({
    blogId: blog.id,
  }));
}

ビルドワーカー数を1に制限

Notion APIにはレート制限(3リクエスト/秒)があるため、ビルド時の並列度を制限する。

// next.config.js
module.exports = {
  experimental: {
    workerThreads: false,
    cpus: 1, // ビルドワーカー数を1に制限
  },
};

API呼び出し間隔の制御

// libs/notion.ts
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

export async function getAllBlogs(): Promise<Blog[]> {
  const pages = [];
  let cursor: string | undefined;
  
  do {
    const response = await notion.dataSources.query({
      data_source_id: DATABASE_ID,
      start_cursor: cursor,
    });
    pages.push(...response.results);
    cursor = response.next_cursor ?? undefined;
    await delay(350); // 350ms間隔でAPI呼び出し
  } while (cursor);
  
  return pages.map(pageToBlog);
}

4. CI/CD — GitHub Actions

ワークフロー定義

# .github/workflows/deploy.yml
name: Deploy to Cloudflare Workers

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - run: npm ci
      
      - name: Build with OpenNext
        run: npx opennextjs-cloudflare
        env:
          NOTION_TOKEN: ${{ secrets.NOTION_TOKEN }}
          NOTION_DATABASE_ID: ${{ secrets.NOTION_DATABASE_ID }}
      
      - name: Deploy to Cloudflare Workers
        run: npx wrangler deploy
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

Worker Secretsの同期

ランタイムで使用する環境変数はWorker Secretsに設定する必要がある。

# Worker Secretsに環境変数を設定
echo "your-notion-token" | npx wrangler secret put NOTION_TOKEN
echo "your-database-id" | npx wrangler secret put NOTION_DATABASE_ID
echo "your-ga-id" | npx wrangler secret put NEXT_PUBLIC_GA_ID

5. Workers有料プランが必要

無料プランの制約

Cloudflare Workers無料プランにはCPU時間10msの制限がある。Next.jsのSSRレンダリングはこの制限を大幅に超えるため、無料プランでは動作しない。

エラー例

Error 1102: Worker exceeded CPU time limit

有料プラン($5/月)

Workers Paid プラン($5/月)にアップグレードすると:

  • CPU時間: 10ms → 30秒

  • リクエスト数: 10万/日 → 1000万/月

  • Workers KV: 読み取り10万/日 → 1000万/月

個人ブログの規模であれば$5/月で十分運用可能。

Tips

📌
Tip 1: esbuildの明示的インストールが必要。OpenNextのビルドプロセスで使用されるが、peer dependencyとして自動インストールされないことがある
📌
Tip 2: Notion APIは350ms間隔で呼び出すとレート制限に引っかからない。getAllBlogsで全記事取得する際はdelay関数を挟む
📌
Tip 3: wrangler devでローカルプレビューする際は.dev.varsファイルに環境変数を設定する
📌
Tip 4: Workers KVのnamespace IDはwrangler.tomlにハードコードするが、APIトークン等のsecretは絶対にwrangler.tomlに書かない

まとめ

  • OpenNextを使えばCloudflare Workers上でNext.jsがNode.js互換で動作する

  • SSG対応はgenerateStaticParamsで全パスを生成し、ビルドワーカー数を制限する

  • CI/CDはGitHub Actions + wrangler deployで簡単に構築可能

  • Workers有料プラン($5/月) が必須。無料プランのCPU 10ms制限では動かない

  • Worker Secretsの設定を忘れると503エラーが大量発生するので注意