Next.js dev serverでCloudflare Service Bindingが503エラーを返す問題の解決方法
CloudflareでNext.jsアプリを開発していて、Service bindingを使った別のWorkerへの通信が、Next.js dev server(npm run dev)で503エラーを返す問題が発生する […]
目次
CloudflareでNext.jsアプリを開発していて、Service bindingを使った別のWorkerへの通信が、Next.js dev server(npm run dev)で503エラーを返す問題が発生することがあります。この記事では、この問題の原因と、開発環境でも正常に動作させるためのフォールバック実装について説明します。
発生する503エラー
Next.js dev serverでService bindingを使ったAPIにアクセスすると、以下のようなエラーが発生します。
HTTP 503 Service Unavailable
Couldn't find a local dev session for the "default" entrypoint of service "your-worker-name" to proxy to
このエラーは、Service bindingが別のWorkerのローカル開発セッションを見つけられないために発生します。具体的には、Service binding経由のfetchが実行されますが、接続先のWorkerが起動していない(またはNext.js dev serverから見えない)ため、503エラーが返されます。
なぜこのエラーが発生するのか
Service bindingは、Cloudflare Workers間の通信を高速化するための仕組みです。本番環境では、Cloudflare内部で自動的にWorker間の接続が確立されます。しかし、ローカル開発環境では状況が異なります。
OpenNextを使ったNext.jsアプリでは、2つの開発サーバーが存在します。
- Next.js dev server(
npm run dev): Next.jsの標準開発サーバーで、高速なホットリロードを提供します。Node.js環境で動作します。 - wrangler dev: Cloudflareのworkerd runtimeで動作する開発サーバーで、本番環境により近い環境を提供します。
Next.js dev serverは、Cloudflare Workersの環境を完全にはエミュレートしません。そのため、Service bindingが別のWorkerのローカル開発セッションを見つけられず、503エラーを返します。一方、wrangler devを使用すると、Service bindingは正常に動作します。これは、wrangler devが各Workerのローカル開発セッションを管理し、Service binding経由で接続できるためです。
この問題が発生するのは、あくまでNext.js dev serverを使用している場合のみです。wrangler devやopennextjs-cloudflare previewを使用している場合は、Service bindingは正常に動作します。
フォールバック実装で動くようにする
この問題を解決するには、Service bindingが503エラーを返した場合に、通常のHTTP fetchにフォールバックする実装を追加します。このアプローチにより、Next.js dev serverでも開発を継続でき、かつ本番環境ではService bindingの高速な通信を維持できます。
フォールバックの流れは以下の3ステップです。
- Service binding経由でfetchを試行する
- 503エラーが返され、エラーメッセージに「Couldn’t find a local dev session」が含まれる場合
- 通常のHTTP fetchにフォールバックする
この実装により、開発環境では通常のHTTP経由でWorkerにアクセスし、本番環境ではService bindingの高速な通信を使用します。
実装コード
以下は、Next.js API RouteでService bindingを使用し、503エラー時にフォールバックする実装例です。
import { NextRequest, NextResponse } from 'next/server';
import { getCloudflareContext } from '@opennextjs/cloudflare';
export async function GET(request: NextRequest) {
const { env } = await getCloudflareContext();
// Service bindingが利用可能かチェック
const ogImageGenerator = env.OG_IMAGE_GENERATOR;
// fetchするURL(localhostを指定)
const ogImageUrl = new URL('http://localhost:8788/generate');
// リクエストヘッダーを準備
const headers = new Headers();
headers.set('Authorization', `Bearer ${env.AUTH_TOKEN}`);
let response: Response;
if (ogImageGenerator) {
// Service binding経由でfetchを試行
response = await ogImageGenerator.fetch(ogImageUrl, { headers });
// 503エラーでローカル開発セッションが見つからない場合
if (!response.ok && response.status === 503) {
const responseBodyText = await response.clone().text().catch(() => '');
if (responseBodyText.includes("Couldn't find a local dev session")) {
// 通常のHTTP fetchにフォールバック
response = await fetch(ogImageUrl, { headers });
}
}
} else {
// Service bindingが存在しない場合は最初から通常のfetch
response = await fetch(ogImageUrl, { headers });
}
return new NextResponse(response.body, {
status: response.status,
headers: response.headers,
});
}
実装のポイント
response.clone()の使用
レスポンスボディを確認するためにresponse.clone()を使用します。Responseオブジェクトのボディは一度しか読み取れないため、cloneしてからテキストを取得します。これにより、元のレスポンスは保持されたまま、エラーメッセージの内容を確認できます。
エラーハンドリング
response.clone().text()が失敗する可能性を考慮し、.catch(() => '')で空文字列を返すようにします。これにより、予期しないエラーでアプリケーション全体が停止することを防ぎます。
セキュリティ上の注意点
フォールバック時のfetchは、必ずlocalhostのみを対象にします。外部のURLへフォールバックすると、認証トークンが意図しないサーバーに送信される危険があります。
// 良い例:localhostを明示的に指定
const ogImageUrl = new URL('http://localhost:8788/generate');
// 悪い例:環境変数から任意のURLを取得
// const ogImageUrl = new URL(env.OG_IMAGE_URL); // 本番URLが設定されている可能性
localhostに限定する理由は、開発環境では開発者のマシン上でWorkerが動作しているためです。本番環境のURLが設定されていると、フォールバック時に本番環境のWorkerにリクエストが送信され、開発用の認証トークンが本番環境に漏洩する可能性があります。
また、本番環境では以下の設定を追加し、localhostへのfetchを無効化することを推奨します。
// wrangler.jsonc または wrangler.toml
{
"compatibility_flags": ["global_fetch_strictly_public"]
}
このcompatibility flagを有効にすると、Workerからprivate IPアドレス(localhostを含む)へのfetchが禁止されます。これにより、フォールバック処理が本番環境で誤って実行されることを防ぎます。
動作確認
実装後、以下の手順で動作を確認します。
- Next.js dev serverを起動(
npm run dev) - Service bindingを使用するAPIエンドポイントにアクセス
- レスポンスが正常に返ることを確認
この時点で、ブラウザの開発者ツールのNetworkタブを確認すると、最初にService binding経由のリクエストが503を返し、その後通常のHTTP fetchが成功していることがわかります。
本番環境での動作確認は、wrangler devやopennextjs-cloudflare previewを使用します。
npm run preview
この場合、Service bindingが正常に動作するため、フォールバック処理は実行されません。Service binding経由のリクエストが最初から成功し、高速な通信が行われます。
本番環境への影響
この実装は、本番環境には影響しません。本番環境では、Service bindingが正常に動作するため、最初のfetchが成功し、フォールバック処理は実行されません。
本番環境でのService bindingの利点は以下の通りです。
- レイテンシがゼロ: Worker間の通信がCloudflare内部で完結し、パブリックなネットワークを経由しません
- 追加コストなし: Service binding経由の通信は、subrequestとしてカウントされますが、追加の課金は発生しません
- セキュリティ: Worker間の通信が外部に公開されないため、認証情報の漏洩リスクが低減します
フォールバック処理は、あくまで開発環境での利便性を向上させるためのものであり、本番環境のパフォーマンスやセキュリティには影響しません。
まとめ
Next.js dev serverでService bindingが503エラーを返す問題は、ローカル開発セッションが見つからないことが原因です。この問題は、503エラー時に通常のHTTP fetchにフォールバックする実装により解決できます。
実装時の重要なポイントは以下の3点です。
- フォールバック処理は、503エラーかつ「Couldn’t find a local dev session」のメッセージが含まれる場合のみ実行する
- フォールバック時のfetchは必ずlocalhostを対象にし、外部URLへのアクセスを避ける
- 本番環境では
global_fetch_strictly_publicフラグを有効にし、localhostへのfetchを禁止する
この実装により、開発環境でも本番環境でも、Service bindingを使った通信が正常に動作します。