ローソク足チャートが表示されたモニター画面
技術解説

Next.js on Cloudflare Workers、本番で詰まる4つの落とし穴

目次を見る

Next.js の App Router で作ったアプリを、Vercel ではなく Cloudflare Workers にデプロイしようとしているチームに向けた内容です。OpenNext(Next.js を Cloudflare や AWS Lambda などのサーバーレス基盤向けに変換するアダプター)を使ったエッジ構成を検討している方の参考になれば幸いです。

AI 画像生成 SaaS を Next.js App Router・Cloudflare Workers・D1(Cloudflare が提供するサーバーレス SQLite)・R2(オブジェクトストレージ)だけで構築した事例があります。常時稼働の VPS や Kubernetes クラスタを避け、コールドスタートなしでグローバルに低レイテンシを狙う構成です。ただしこの構成、素直に next build した成果物をそのまま載せ替えるだけでは動きません。

何が起きるか:単一 Worker がすぐ詰まる

SSR(サーバーサイドレンダリング)、画像処理、LLM によるオーケストレーション、Webhook 受信を全部ひとつの Worker に詰め込むと、実行時間やメモリの上限にすぐぶつかります。

具体的には、画像生成の進捗待ちや外部 API への長時間リクエストが SSR のリクエストハンドラと同じプロセスで走り、タイムアウトやメモリ超過でリクエストごと失敗する事象が起きます。症状としては「ローカルでは動くのに Cloudflare にデプロイすると 502 や謎のタイムアウトが出る」という形で表面化します。

影響範囲は画像生成のような重い処理に限りません。D1 への大量クエリや R2 への大きめのファイルアップロードを SSR の中で直列に待つだけでも、同じ Worker の実行時間制限に触れる可能性があります。

なぜ起きるか:Workers のランタイム制約を順に分解する

原因は大きく3段階に分けて理解すると整理しやすくなります。

第一に、Cloudflare Workers は Node.js ランタイムではなく独自の V8 ベースの軽量ランタイムです。Node.js 標準モジュールの一部が未対応、またはポリフィル(代替実装)経由でしか動かないため、Next.js のミドルウェアやサーバーコンポーネントが暗黙に依存しているライブラリが動かないことがあります。

第二に、Worker には実行時間とメモリの上限があります。AI 画像生成のように外部 API の応答を数秒〜数十秒待つ処理を、SSR のレスポンスパスにそのまま組み込むと、ユーザーからのリクエストそのものがこの上限に縛られます。

第三に、OpenNext は Next.js のビルド成果物を Workers 互換の形に変換しますが、変換できるのはあくまで「リクエスト応答型」の処理です。バックグラウンドでのジョブキューイングや長時間のワークフロー管理までは面倒を見てくれません。ここを自前で設計しないと、1つの Worker に責務が集中してしまいます。

実例となった構成では、この問題をサイト表示用の Main Site Worker と、画像生成やキャラクター継続性の管理などを担当する複数の Worker、さらに8つのキュー(Queue、非同期メッセージを溜めて順次処理する仕組み)に分割することで解決しています。SSR のリクエストはすぐ返し、重い処理はキュー経由で別 Worker に渡す形です。

自分のプロジェクトが該当するか確認する

次の観点で、今のプロジェクトが同じ落とし穴にはまりやすいかを確認できます。

  • next.config.js に output: "standalone" や Edge Runtime 指定があるか。Node.js API 依存のコードが残っていないか grep で確認する
  • API Route や Server Action の中で fetch 以外の外部通信ライブラリ(画像処理系・PDF 生成系など)を使っていないか
  • 1リクエストあたりの処理時間が数秒を超える箇所がないか。ログに応答時間を仕込んで確認する
  • wrangler.toml(Cloudflare Workers の設定ファイル)に Queues や Durable Objects のバインディングが定義されているか

wrangler.toml を開いて [[queues.producers]] や [[durable_objects.bindings]] のセクションがなければ、現状は単一 Worker 構成の可能性が高いです。重い処理を含むプロジェクトなら、ここが最初のチェックポイントになります。

バージョン面では、OpenNext の Cloudflare アダプター(@opennextjs/cloudflare)と Next.js のバージョン互換が頻繁に更新されます。package.json の該当パッケージのバージョンと、OpenNext の公式リポジトリの Compatibility テーブルを突き合わせる確認が必須です。

対策の手順

責務分離とキュー導入は、次の手順で段階的に進められます。

# 1. 現状の Worker 構成を確認
npx wrangler whoami
cat wrangler.toml

# 2. OpenNext と Next.js のバージョン整合を確認
npm ls next @opennextjs/cloudflare

# 3. Queues を使う場合はローカルで有効化して動作検証
npx wrangler queues create image-generation-queue

手順としては以下の流れになります。

  • SSR と重い処理(画像生成・LLM 呼び出し・Webhook 受信)を洗い出し、処理時間でグルーピングする
  • 重い処理ごとに専用 Worker を切り出し、wrangler.toml に [[queues.producers]] と [[queues.consumers]] を追加する
  • SSR 側の Worker からはキューにメッセージを投げるだけにし、即座にレスポンスを返す設計に変える
  • D1 への書き込みは生成完了後の非同期処理側に寄せ、SSR パスでは読み取り専用クエリに留める
  • R2 へのアップロードも同様にバックグラウンド Worker に移し、進捗はポーリングか WebSocket で別途通知する
SSR のリクエストパスに「数秒以上かかる可能性がある処理」を1つでも残さないことが、Workers 移行で最初に守るべき境界線です。

この境界線を意識せずに移行すると、ローカルの Node.js 環境では問題なかったコードが、本番の Workers 環境でだけ謎のタイムアウトを起こす原因になります。Vercel の Edge Functions でも似た制約があるため、Vercel から Cloudflare への移行を検討している場合は両方のランタイム制限表を並べて確認しておくと安心です。

確認と対策のまとめ

Cloudflare Workers への移行で起きやすい落とし穴は、単一 Worker への責務集中に集約されます。

  • wrangler.toml に Queues や Durable Objects の定義がなければ、重い処理が SSR パスに混在している可能性を疑う
  • OpenNext と Next.js のバージョン互換は、デプロイ前に公式 Compatibility テーブルで必ず照合する
  • 画像生成や外部 API 呼び出しなど時間のかかる処理は、SSR 用 Worker とは別の Worker とキューに切り出す
  • D1 の書き込みと R2 のアップロードも、できる限り非同期処理側に寄せてリクエストパスを軽くする

まずは wrangler.toml を開いて現状の Worker 構成を可視化するところから始めてみてください。

参考

How I Built an Edge-First AI Cartoon SaaS with Next.js, Cloudflare Workers & D1

この記事について: 本記事は AI を活用して作成し、forva AI 編集部が内容を確認・監修しています。

AI 駆動開発のご相談は forva AI へ。まずはお気軽にどうぞ。