はじめに
OpenNext × Cloudflare Workers 環境で、SSG を維持しつつ Stripe / Supabase の 環境変数・シークレット起因でビルドが落ちた件で3時間もハマったため、次はすぐに解決できるよう未来の自分のためにここにまとめておきます。
鍵は 「ビルド時に見える値」 と 「実行時だけ見える値」 の切り分け、そして SDK のトップレベル初期化を避けることです。
問題
まずは起きた問題から。
Stripe
Error: Neither apiKey nor config.authenticator provided
原因: ビルド中に new Stripe(process.env.STRIPE_SECRET_KEY) が トップレベルで実行され、Secret が未注入(undefined)だった。
Supabase
Error: @supabase/ssr: Your project's URL and API key are required
原因: SSG(generateStaticParams など) の最中に NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEY が未定義。
※ ビルド工程は wrangler の実行時変数を参照しないため、別途 “ビルド時にも見える方法” で渡す必要があるという感じですね。
解決方法
- Stripe は関数内で初期化(トップレベル禁止)
Workers では httpClient も必須。
// app/api/checkout/webhook/route.ts など
import Stripe from "stripe";
export const runtime = "edge";
export async function POST(req: Request) {
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
httpClient: Stripe.createFetchHttpClient(),
});
// ...
}
ページで直接 Stripe を触る必要があるなら API ルート経由にし、ページ側は fetch のみに絞ることが肝ですね。
- SSG で必要な “公開キー” をビルド時にも見える形で供給
NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEY は 公開前提(supabase側でRLSで保護) のため、ビルド工程にも注入する。
Cloudflare Dashboard にあるビルドの欄にある【変数とシークレット】のところに Text で登録しておくこと

Supabase クライアントも “関数内生成” を徹底
import { createServerClient } from "@supabase/ssr";
export async function generateStaticParams() {
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{ /* cookies 等 */ }
);
const { data } = await supabase.from("products").select("id");
return data?.map(p => ({ id: p.id })) ?? [];
}
Service Role などの機密キーは引き続き Secret(実行時のみ) に置いてください。
おわりに
公開キーはビルド時にも見えるように注入しておく必要がある
秘密キーは実行時のみに絞ること(Secret)。
SDK 初期化は必ず関数スコープ。
これで OpenNext × Cloudflare Workers でも SSG を崩さず 安定してビルド・実行できます。
これがわかるまで3時間ほどハマりました。