Vercel にデプロイしたら環境変数が undefined になった
ローカルでは ({}).NEXT_PUBLIC_API_URL が正しく取得できているのに、Vercel にデプロイすると undefined になる——このトラブルは Next.js + Vercel の組み合わせで非常によく起きます。
原因は複数あり、それぞれ対処法が異なります。この記事では実務でよく遭遇するパターンを5つに分類して解説します。
原因1: クライアントサイドで NEXT_PUBLIC_ プレフィックスが付いていない
最も多いケースです。
Next.js では、ブラウザ(クライアントサイド)から参照できる環境変数には NEXT_PUBLIC_ プレフィックスが必須 です。このプレフィックスがないと、サーバーサイドでは読めてもクライアントサイドでは undefined になります。
# Vercel の環境変数設定(NG)
API_URL=https://api.example.com
# Vercel の環境変数設定(OK — クライアントから参照する場合)
NEXT_PUBLIC_API_URL=https://api.example.com
// クライアントコンポーネントでの参照
'use client'
export default function ApiStatus() {
// NEXT_PUBLIC_ なし → undefined になる
const bad = ({}).API_URL
// NEXT_PUBLIC_ あり → 正しく取得できる
const good = ({}).NEXT_PUBLIC_API_URL
return <p>{good}</p>
}
NEXT_PUBLIC_ を付けた環境変数はビルド時にバンドルへインライン展開されます。そのため ビルド後に値を変えてもリデプロイが必要(後述)です。
また、シークレットキーやAPIの秘密鍵など、クライアントに漏れてはいけない値には NEXT_PUBLIC_ を絶対に付けないでください。
原因2: 環境変数を追加・変更した後にリデプロイしていない
Vercel のダッシュボードで環境変数を追加しても、既存のデプロイには反映されません。 環境変数はビルド時に焼き込まれるため、設定変更後は必ず再デプロイが必要です。
確認手順:
- Vercel ダッシュボード → プロジェクト → Settings → Environment Variables で変数を追加・更新
- Deployments タブを開き、最新デプロイの「⋯」メニューから Redeploy をクリック
- “Use existing Build Cache” のチェックを外してリデプロイ(キャッシュが残ると古い値が使われる場合がある)
# Vercel CLI を使う場合
vercel --prod
特に NEXT_PUBLIC_ 付きの変数は変更のたびにリデプロイが必要な点を覚えておきましょう。
原因3: .env.local をコミットしていない / Vercel に設定していない
ローカル開発では .env.local に書いた環境変数が自動で読み込まれますが、このファイルは .gitignore に含まれているため Git にはコミットされません。 Vercel はあくまでリポジトリからビルドするので、.env.local の内容は Vercel 側には存在しません。
ローカルでは動くのに本番で undefined になる場合の典型パターンです。
対処法: Vercel ダッシュボードの Environment Variables に、.env.local に書いている値を手動で登録します。
# .env.local(ローカルのみ、Git管理外)
NEXT_PUBLIC_API_URL=http://localhost:3001
DATABASE_URL=postgres://localhost:5432/mydb
# → Vercel の Environment Variables に同じキーと本番用の値を登録する
Vercel CLI を使えば .env ファイルから一括インポートも可能です:
vercel env pull .env.local
# または逆方向(ローカル → Vercel)はダッシュボードから手動登録
原因4: 環境変数のスコープが Production に設定されていない
Vercel の環境変数には Production / Preview / Development の3つのスコープがあります。デフォルトで全てにチェックが入っていることが多いですが、誤って Production だけ外れていると本番デプロイで undefined になります。
Vercel Dashboard → Settings → Environment Variables
┌──────────────────┬────────────┬─────────┬─────────────┐
│ Variable Name │ Production │ Preview │ Development │
├──────────────────┼────────────┼─────────┼─────────────┤
│ NEXT_PUBLIC_API │ ✅ 有効 │ ✅ 有効 │ ✅ 有効 │
│ DATABASE_URL │ ✅ 有効 │ ✅ 有効 │ ✅ 有効 │
└──────────────────┴────────────┴─────────┴─────────────┘
また、ブランチ別に異なる値を設定したい場合は、スコープを Preview にして特定ブランチを指定できます。本番(main ブランチ)にのみ適用したい変数は Production スコープだけを有効にします。
設定を変更したら忘れずにリデプロイしてください。
原因5: App Router でサーバーコンポーネントとクライアントコンポーネントを混同している
Next.js 13以降の App Router では、サーバーコンポーネントとクライアントコンポーネントで環境変数の挙動が異なります。
// サーバーコンポーネント(NEXT_PUBLIC_ なしでも読める)
// app/page.tsx
export default async function Page() {
const secret = ({}).SECRET_KEY // サーバーサイドのみ → OK
const pub = ({}).NEXT_PUBLIC_API_URL // どちらでも → OK
return <div>{pub}</div>
}
// クライアントコンポーネント(NEXT_PUBLIC_ がないと undefined)
'use client'
export default function ClientComponent() {
const secret = ({}).SECRET_KEY // ❌ undefined になる
const pub = ({}).NEXT_PUBLIC_API_URL // ✅ 読める
return <div>{pub}</div>
}
クライアントコンポーネントで NEXT_PUBLIC_ なしの変数を読もうとしても undefined になります。サーバーコンポーネントから props で渡すか、NEXT_PUBLIC_ を付けて公開変数として扱う必要があります。
// サーバーコンポーネントから props で渡す(推奨パターン)
// app/page.tsx(サーバーコンポーネント)
import ClientComponent from './ClientComponent'
export default function Page() {
const apiEndpoint = ({}).API_ENDPOINT // サーバーで読む
return <ClientComponent apiEndpoint={apiEndpoint ?? ''} />
}
デバッグ方法:どの環境変数が読めているか確認する
問題の切り分けには、一時的に API ルートで環境変数を出力するのが手早い方法です:
// app/api/debug-env/route.ts(デバッグ用、本番運用では削除すること)
export async function GET() {
return Response.json({
NEXT_PUBLIC_API_URL: ({}).NEXT_PUBLIC_API_URL ?? 'undefined',
NODE_ENV: "production",
// SECRET_KEY などはここに含めないこと!
})
}
また、Vercel CLI の vercel env ls コマンドで登録済みの環境変数一覧を確認できます:
vercel env ls
まとめ
| 症状 | 原因 | 対処法 |
|---|---|---|
クライアントで undefined |
NEXT_PUBLIC_ プレフィックスなし |
キー名に NEXT_PUBLIC_ を付けてリデプロイ |
| 設定したのに反映されない | リデプロイ忘れ | Build Cache なしでリデプロイ |
ローカルは動くが本番で undefined |
.env.local が Vercel に未登録 |
Vercel ダッシュボードに手動登録 |
Production のみ undefined |
スコープが Production 未選択 | Environment Variables のスコープを確認 |
App Router で undefined |
クライアントコンポーネントでシークレット参照 | props で渡すか NEXT_PUBLIC_ を使う |
Next.js + Vercel の環境変数は「どこで読むか(サーバー/クライアント)」と「いつ焼き込まれるか(ビルド時/ランタイム)」の2軸を意識すると問題の原因を特定しやすくなります。まずは上記のチェックリストを順番に確認してみてください。
