Cloudflare Pages Functionsの環境変数が本番に反映されない原因と直し方

Cloudflare Pages Functionsの環境変数が本番に反映されない原因と直し方

※本記事にはプロモーション(広告・アフィリエイトリンク)を含みます。

ダッシュボードで環境変数を追加・変更したのに、本番の Cloudflare Pages Functions では古い値のまま、あるいは undefined になる——。この記事は、その原因を「ビルド時かランタイムか」「どの環境に設定したか」「どこから読んでいるか」の3点で切り分け、再現できる手順で直すためのものです。

まず結論から言うと、原因のほとんどは次の3つに収れんします。(1) 環境変数を変えたのに再デプロイしていない、(2) Production と Preview でスコープが分かれている、(3) process.env から読んでいて context.env を使っていない。順に見ていきます。

まず切り分ける: ビルド時かランタイムか

「環境変数が効かない」と言っても、効かせたい場所が2か所あります。ここを混同すると直せません。

種類 効くタイミング 例 設定場所
ビルド時変数 npm run build などビルド中 フレームワークがコードに埋め込む値 Pages の「変数とシークレット」
ランタイム変数 リクエストを処理する瞬間 Functions 内で読むAPIキー等 同上(ただし読み方が違う)

見分け方はシンプルです。ブラウザに配信される静的な HTML/JS の中に値が入っているなら、それはビルド時に確定した値です。functions/ 以下(または _worker.js)のサーバー側コードで読む値なら、ランタイム変数です。どちらが反映されていないのかを先に決めてください。

原因1: 変更したのに再デプロイしていない

いちばん多いのがこれです。Cloudflare の公式ドキュメントによると、環境変数やシークレットを追加・変更しても、その変更は新しいデプロイに対してのみ適用されます。すでに公開中のデプロイに遡って反映されるわけではありません。

つまり、ダッシュボードで値を保存しただけでは本番は変わりません。次のどちらかで新しいデプロイを作ります。

# Git 連携なら空コミットで再デプロイを走らせる
git commit --allow-empty -m "chore: trigger redeploy for env vars"
git push

Git を使わず直接アップロードしている場合は Wrangler で再デプロイします。

npx wrangler pages deploy ./dist --project-name=<your-project>

ダッシュボードの「Deployments」から既存デプロイを Retry deployment / Rollback ではなく、新規のビルドとして 作り直す点に注意してください。古いデプロイを再試行しても、ビルド時変数は当時の値で焼き直されることがあります。

原因2: Production と Preview は別のスコープ

Cloudflare Pages の環境変数は、公式ドキュメントのとおり Production と Preview で別々に管理されます。本番ブランチ(通常は main など、プロジェクトで指定した本番ブランチ)へのデプロイは Production、それ以外のブランチや PR プレビューは Preview の変数を読みます。

ありがちな失敗は、Preview 側にだけ値を入れていて Production に入れ忘れているケース、あるいはその逆です。両方で同じ値が必要なら、両方のスコープに登録する必要があります。

[変数とシークレット]
  Production:  API_BASE = https://api.example.com   ← 本番で読む
  Preview:     API_BASE = https://staging.example.com ← プレビューで読む

ローカルの wrangler pages dev で動くのに本番で動かない、という場合もここを疑います。ローカルは .dev.vars ファイルを読むため、本番のスコープ設定とは独立しているからです。.dev.vars は KEY=value の形式で、コミットしないよう .gitignore に入れておきます。ローカル実行まわりでポートが取れずに詰まるときは、wrangler devがAddress already in useで起動しない時のポート変更手順も合わせて確認してください。

原因3: context.env から読めているか

Pages Functions は Workers ランタイムで動くため、環境変数は Node.js の process.env ではなく、ハンドラに渡される context.env から読むのが基本です。公式ドキュメントによると、functions/ の関数は onRequest(context) の形で呼ばれ、context.env にバインディングと環境変数が入ります。

// functions/api/hello.js
export async function onRequest(context) {
  const key = context.env.API_BASE; // ここから読む
  if (!key) {
    return new Response("env missing", { status: 500 });
  }
  return new Response(key);
}

_worker.js を直接置くアドバンスドモードなら、fetch(request, env, ctx) の第2引数 env が同じ役割です。

export default {
  async fetch(request, env, ctx) {
    return new Response(env.API_BASE ?? "env missing");
  }
};

process.env.XXX を使いたい場合は、nodejs_compat 互換フラグと適切な互換性日付(compatibility date)の設定が前提になります。ここは設定やランタイムのバージョンによって挙動が変わりうるため、まずは確実に動く context.env / env 経由に寄せることをおすすめします。環境によって異なる部分です。

なお、NEXT_PUBLIC_ で始まる変数のようにビルド時にクライアントコードへ埋め込まれる種類の値は、ランタイムで context.env に入れても後から差し替えられません。これは原因1で触れたビルド時変数に該当し、値を変えたら必ずビルドし直す必要があります。Next.js(App Router)のような構成では、この「クライアントに埋め込まれる値」と「サーバーでしか読めない値」の切り分けでつまずきやすいため、ここは最初に意識しておくと安全です。

確認手順の早見表

上から順にチェックすると、たいていどこかで止まります。

手順 確認すること よくある見落とし
1 変更後に新しいデプロイを作ったか 保存しただけで満足している
2 Production に入れたか Preview だけに入っている
3 context.env から読んでいるか process.env のまま
4 ビルド時変数ではないか クライアント埋め込み値を実行時に変えようとしている
5 ローカルと本番を混同していないか .dev.vars では動くが本番未設定

まとめ

「環境変数が本番に反映されない」は、ほぼ 再デプロイ漏れ・スコープ違い・読み方違い の3つに分解できます。次の一手として、まずは原因1を潰してください——ダッシュボードで値を確認したら、空コミットでもよいので新しいデプロイを1回走らせ、本番ブランチ(Production スコープ)に反映されるかを見ます。これで直らなければ原因2→3の順で切り分ければ、どこで値が落ちているかが特定できます。

よくある質問

Cloudflareで環境変数を保存したのに本番が変わらないのはなぜ?

公式ドキュメントによると、環境変数の変更は新しいデプロイにのみ適用され、公開中のデプロイには遡及しません。空コミットなどで再デプロイを走らせれば反映されます。

ProductionとPreviewの環境変数は共有されますか?

共有されません。Pagesの環境変数はProductionとPreviewで別々に管理され、本番ブランチへのデプロイはProductionの値を読みます。両方で必要なら両スコープに登録します。

Pages Functionsでprocess.envが使えないのはなぜ?

FunctionsはWorkersランタイムで動くため、基本はonRequestのcontext.envから読みます。process.envはnodejs_compatフラグなどの設定が前提で、環境により挙動が変わります。