🍞

🍞 Next.js を OpenNext で Cloudflare Workers にデプロイする

に公開

この記事でわかること

  • なぜ Pages ではなく Workers + OpenNext に移行したのか
  • workerへのnextjsデプロイ方法
  • OpenNext × Cloudflare の最短キャッシュ実装
  • 小規模PJでの推奨構成・設定例

背景:Pagesの限界と SSR 導入

  • 今回、ユーザープロフィール👥(例:/{username})などでサーバーサイドのデータ取得(Server Component の server fetch、動的パラメータ)を導入しました。これにより、静的エクスポートができないのでSSR前提で.nextを指定してのデプロイになるのですが、以下の問題が発生します。

⚠️Cloudflare Pages には単一アセット 25MBの制限⚠️

  • OpenNext の Cloudflare アダプタを使えば、Next.js のビルドを Workers で実行する形へ変換でき、SSR/ISR を含む配信が可能になります。これによりキャッシュも軽量に考慮したSSRでのCloudflareデプロイが可能になります。25MBの制限関係なく!🧙
手法 特徴 メリット デメリット
SSG ビルド時に静的HTML生成 超高速、SEOに強い 更新に再ビルド必須
ISR SSG + 有効期限で再生成 高速+更新性 再生成の仕組みが複雑
SSR リクエストごとに生成 常に最新、柔軟 レイテンシ・コスト増

とりあえずopennextjsでworkerにデプロイまで行う🚀

前提

  • Next.js 15.x
  • Cloudflare アカウントと Wrangler CLI

1. パッケージの導入

  • opennextを追加します📦
# OpenNext Cloudflare adapter
bun add @opennextjs/cloudflare

2. package.json にスクリプト追加

  • buildやdeployのスクリプト入れておきます。🚀
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview",
    "deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy"
  }
}

3. wrangler.jsonc の設定

  • プロジェクト直下に wrangler.jsonc を用意します。
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-next-app", // 書き換えてください
  "main": ".open-next/worker.js",
  "compatibility_date": "2025-01-01", // 互換性用のメモ 現在の日付📆に
  "compatibility_flags": [
    "nodejs_compat"
  ],
  "assets": {
    "directory": ".open-next/assets",
    "binding": "ASSETS"
  }
}

4. OpenNext 設定(ISR を使う場合)

  • ISR(Incremental Static Regeneration)を使うなら、R2 バケットをキャッシュ用に用意しておきます。⚡
// open-next.config.ts  ルートにおいてください。
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";

export default defineCloudflareConfig({
  incrementalCache: r2IncrementalCache,
});
  • wrangler.jsonc に R2 バインディング
wrangler.jsonc
{
  "r2_buckets": [
    {
      "binding": "NEXT_INC_CACHE_R2_BUCKET",
      "bucket_name": "next-inc-cache"
    }
  ]
}
  • これで ISR のキャッシュが R2 に保存され、安定運用ができます。

静的アセットのキャッシュも忘れないうちにやっておきましょう

  • public/配下に_headersという普通のファイル作ってください。1年間の長期キャッシュ設定
/_next/static/*
  Cache-Control: public,max-age=31536000,immutable

5. デプロイ

bun run deploy

ISR (Incremental Static Regeneration)って何?

  • Next.js が持つ SSG の拡張版です。
  • ビルド時に静的 HTML を生成するのは同じ何ですけど、ページごとに「有効期限(revalidate 秒数)」を設定できて動的なデータ返却が可能になります。
  • 有効期限が切れたページにリクエストが来たとき:
    👉キャッシュ済みの古いページを即返す(ユーザーは待たされない)
    👉裏で新しい HTML を生成してキャッシュを更新
    👉次のユーザーからは新しい HTML を受け取れる
    特徴
  • メリット: 静的の速さと、更新の柔軟さを両立できる
  • デメリット: 初回の再生成リクエストが遅れる場合あり。複雑なキャッシュ管理が必要

ISR OpenNext+Cloudflare Workers Cache

OpenNext + Cloudflare Workers の場合、ISR の「キャッシュ保存先」として Cloudflare の仕組みを利用します。

  • Incremental Cache (R2 など) に HTML を保存
  • Queue (Durable Objects) が「どのページを再生成するか」を管理
  • Tag Cache (D1/DO) が revalidateTag のような任意の無効化に対応
    つまり Cloudflare が「ISR の裏方倉庫と配達員」みたいな役割を果たしています。

アナロジーでまとめてみる

  • SSG: 「パン屋🍞で全部のパンを朝焼いて並べておく」 → 常に速いけど品切れ更新が面倒
  • ISR: 「朝焼いたパン🍞を並べつつ、◯分ごとに人気パンだけ焼き直して補充」 → 品質効率の両立
  • Cloudflare のキャッシュ: 「街中にあるパンの置き倉庫🏭」 → ユーザーに近い倉庫からすぐ配達できる

推奨構成(小規模)

  • Incremental Cache:R2

wrangler.jsonc

{
  "name": "<WORKER_NAME>",
  "main": ".open-next/worker.js",
  "compatibility_date": "2024-12-30",
  "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"],

  "assets": { "directory": ".open-next/assets", "binding": "ASSETS" },

  "services": [
    { "binding": "WORKER_SELF_REFERENCE", "service": "<WORKER_NAME>" }
  ],

  // R2 incremental cache
  "r2_buckets": [
    { "binding": "NEXT_INC_CACHE_R2_BUCKET", "bucket_name": "<BUCKET_NAME>" }
  ]
}

open-next.config.ts

import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";

export default defineCloudflareConfig({
  incrementalCache: r2IncrementalCache,
});

R2 に Incremental Cache を置くだけでも、まずは ISR を動かせます。
Queue や Tag Cache は必要になったら導入すればOK。

  "r2_buckets": [
    {
      "binding": "NEXT_INC_CACHE_R2_BUCKET", // 変数名
      "bucket_name": "hogehoge-cache" // 新しくcache用のバケット作成
    }
  • 画像バケットなどと混同すると、CORS/認可/権限などがごちゃつきます。非公開にしてworkersからのみアクセスできるようにした方がいいです。

最後に

最小ステップ

Pages → Workers(OpenNext)
1. @opennextjs/cloudflare と wrangler を導入
2. wrangler.jsonc を用意(main: .open-next/worker.js、bindings など)
3. open-next.config.ts で R2/Queue/Tag Cache を設定
4. package.json に

build: next build
preview: opennextjs-cloudflare build && opennextjs-cloudflare preview
deploy:  opennextjs-cloudflare build && opennextjs-cloudflare deploy

を追加
5. public/_headers で静的アセットの長期キャッシュ
6. bun run deployでリリース

Discussion