Cloudflare Workers で env(環境変数)が undefined になる・読めない時の原因と対策【process.env / .dev.vars / .env / wrangler secret put】
公開日:2026-05-04/カテゴリ:Cloudflare・バックエンド・インフラ/AnchorUp 技術ブログ
Cloudflare Workers で環境変数が読めなくてハマった
Cloudflare Workers / Pages に移行した直後によくある問題が「環境変数が undefined になる」というものです。Node.js や Vercel に慣れていると、同じ感覚で .env ファイルを作ったり process.env を使ったりしてしまいますが、Cloudflare Workers の実行環境は全く異なります。
この記事では、環境変数が読めない5つの原因と、それぞれの対処法を解説します。
結論: Cloudflare Workers の env はどこから取得するのか?
Cloudflare Workers の環境変数(env)は、Node.js のようなグローバルの process.env ではなく、fetch ハンドラの第2引数 env(または import { env } from "cloudflare:workers")から取得します。 値は wrangler.toml / wrangler.jsonc の vars、wrangler secret put、ダッシュボードのいずれかで登録したものだけが入ります。
| 取得方法 | 書き方 | 使える条件 |
|---|---|---|
| ハンドラ第2引数(基本) | fetch(request, env) { env.API_KEY } |
常に使える |
| Hono | c.env.API_KEY |
常に使える |
| グローバル import | import { env } from "cloudflare:workers" |
ハンドラの外(モジュールのトップレベルや別ファイル)から参照したい時 |
process.env |
process.env.API_KEY |
nodejs_compat フラグ有効 かつ compatibility_date が 2025-04-01 以降の時だけ値が入る |
| 登録場所 | 用途 | 反映される環境 |
|---|---|---|
wrangler.toml / wrangler.jsonc の vars |
平文の設定値 | ローカル・本番の両方 |
.dev.vars または .env |
ローカル用シークレット | wrangler dev のみ(本番には送られない) |
wrangler secret put |
本番シークレット | 本番(デプロイ済み Worker) |
| ダッシュボード「Variables and Secrets」 | Pages / Workers の本番値 | 本番 |
「ローカルでは読めるのに本番で undefined」なら本番側の登録漏れ、「本番では読めるのにローカルで undefined」なら .dev.vars / .env 側の問題、と切り分けるのが最短です。
こんな症状が出ていませんか?
env.API_KEYがundefinedになるprocess.env.MY_VARがundefinedになる- ローカル(
wrangler dev)では動くが、本番 Worker ではundefinedになる .envファイルに書いたのに全く反映されない- TypeScript で
envオブジェクトの型エラーが出る
原因1: なぜ Cloudflare Workers では process.env が undefined になるのか?
これが最もよくある原因です。 Node.js に慣れているエンジニアが最初にハマるポイントです。結論として、process.env に値が入るのは「nodejs_compat 有効 + compatibility_date が 2025-04-01 以降」の組み合わせの時だけで、それ以外は env 引数を使う必要があります。
Cloudflare Workers の実行環境は Node.js ではなく、V8 Isolates という独自ランタイムです。Node.js 互換フラグがない状態では process は Node.js と同じようには用意されておらず、process.env 経由では環境変数を読めません(ビルドツールの設定次第で undefined になったり ReferenceError: process is not defined になったりします)。
// ❌ Node.js の感覚で書くと undefined になる
export default {
fetch(request: Request) {
const apiKey = process.env.API_KEY // undefined!
return new Response(apiKey)
},
}
Workers では、環境変数はハンドラ関数の第2引数の env オブジェクトから取得します。
// ✅ Workers での正しい環境変数の取得方法
export default {
fetch(request: Request, env: Env) {
const apiKey = env.API_KEY // 正しく取得できる
return new Response(apiKey)
},
}
Hono を使っている場合は c.env から取得します。
// ✅ Hono での環境変数の取得
import { Hono } from 'hono'
type Bindings = {
API_KEY: string
DB: D1Database
}
const app = new Hono<{ Bindings: Bindings }>()
app.get('/api', (c) => {
const apiKey = c.env.API_KEY // 正しく取得できる
return c.text(apiKey)
})
ハンドラの外で env を使いたい時は cloudflare:workers から import する
DB クライアントの初期化を別ファイルに切り出した時など、「env をバケツリレーで渡すのが面倒」というケースでは、cloudflare:workers モジュールからグローバルな env を import できます。
// src/lib/config.ts
import { env } from 'cloudflare:workers'
// ハンドラの外(モジュールのトップレベル)からでも参照できる
export const apiBaseUrl = env.API_BASE_URL
process.env をどうしても使いたい場合(既存ライブラリ対応など)
process.env を前提にした npm ライブラリを動かしたい場合は、nodejs_compat を有効にし、compatibility_date を 2025-04-01 以降にします。この日付以降は nodejs_compat_populate_process_env がデフォルトで有効になり、vars やシークレットが process.env にも入ります。
# wrangler.toml
compatibility_date = "2025-04-01" # これより古い日付だと process.env は空のまま
compatibility_flags = ["nodejs_compat"]
compatibility_date が古いままのプロジェクトで「nodejs_compat を付けたのに process.env が空」になるのはこのためです。nodejs_compat 自体が効かない・Node.js モジュールでエラーになる場合は Cloudflare Workers で nodejs_compat を設定しても動かない時の解決策 も参照してください。
原因2: wrangler.toml にバインディングが設定されていない
env オブジェクトから変数を取得するためには、wrangler.toml(または wrangler.jsonc)にバインディングとして登録する必要があります。.env ファイルは本番の Worker には一切送られないため、本番で使う平文の値は必ずここに書きます。
# wrangler.toml
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"
# 環境変数(平文で書いてOKなもの)
[vars]
API_BASE_URL = "https://api.example.com"
APP_ENV = "production"
wrangler.jsonc を使っている場合は同じ内容を vars キーに書きます。
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-04-01",
"vars": {
"API_BASE_URL": "https://api.example.com",
"APP_ENV": "production"
}
}
よくあるミスとして、変数名のタイポや、[vars] セクションの書き忘れがあります。
# ❌ よくある間違い: セクション名のタイポ
[var] # 正しくは [vars]
API_KEY = "xxx"
# ❌ よくある間違い: 変数名の不一致
[vars]
api_key = "xxx" # コードでは env.API_KEY と書いているのに小文字で登録
本番環境と開発環境でバインディングの値を分けたい場合は、[env.staging] のような環境別セクションを使います。
# 本番用
[vars]
APP_ENV = "production"
API_BASE_URL = "https://api.example.com"
# ステージング用
[env.staging]
[env.staging.vars]
APP_ENV = "staging"
API_BASE_URL = "https://api-staging.example.com"
デプロイ時に環境を指定するには --env フラグを使います。
wrangler deploy --env staging
原因3: ローカル(wrangler dev)で .dev.vars / .env の値が読めない
wrangler dev でローカル開発する際、シークレット(API キーなど機密情報)はプロジェクトルートの .dev.vars か .env に書きます。古い記事では「.env は読まれない」と書かれていることがありますが、現在の Wrangler は .env も読み込みます。ただし両方置くと .env は無視されるのが最大の落とし穴です。
| 状況 | ローカルの env に入る値 |
|---|---|
.dev.vars だけある |
.dev.vars の値 |
.env だけある |
.env 系ファイルの値 |
| 両方ある | .dev.vars の値のみ(.env は丸ごと無視) |
| どちらもない | wrangler.toml の vars のみ |
「.env に書いたのに undefined」の場合、昔作った .dev.vars が残っていないか確認してください。Cloudflare 公式ドキュメントも「どちらか一方だけを使う」ことを推奨しています。
.env を使う場合の優先順位(上ほど強い)は次のとおりです。--env staging のように環境を指定した時は .env.staging 系も読まれます。
.env.<環境名>.local
.env.local
.env.<環境名>
.env
一方 .dev.vars は、.dev.vars.<環境名> があると .dev.vars はマージされず丸ごと置き換えになります。共通の値を .dev.vars に書いて差分だけ .dev.vars.staging に書く、という使い方はできない点に注意してください。
また、.env を Vite など別ツールと共用していて Wrangler に読ませたくない場合は、環境変数 CLOUDFLARE_LOAD_DEV_VARS_FROM_DOT_ENV=false で .env の読み込みを無効化できます。
.dev.vars の書き方は .env と同じ KEY=VALUE 形式です。
# .dev.vars(シークレットをローカル開発用に設定)
API_KEY=sk-dev-xxxxxxxxxxxx
DATABASE_URL=postgresql://localhost:5432/mydb
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxx
必ず .gitignore に追加してください。 .dev.vars / .env はシークレットを含むため、Git に含めてはいけません。
# .gitignore に追加
echo ".dev.vars*" >> .gitignore
echo ".env*" >> .gitignore
wrangler.toml の [vars] はローカル開発時にも env オブジェクトに入りますが、シークレットは .dev.vars に書くことでセキュリティを確保します。
原因4: シークレットは wrangler secret put で登録が必要
本番環境のシークレット(API キー、データベースパスワードなど)は、wrangler.toml の [vars] に平文で書いてはいけません。wrangler.toml は通常 Git に含めるため、機密情報が漏洩するリスクがあります。
本番環境のシークレットは wrangler secret put コマンドで登録します。
# シークレットを対話形式で登録
wrangler secret put API_KEY
# プロンプトが出るので値を入力(画面には表示されない)
# パイプで値を渡すこともできる
echo "sk-prod-xxxxxxxxxxxx" | wrangler secret put API_KEY
登録済みのシークレット一覧を確認するには wrangler secret list を使います。
wrangler secret list
# NAME TYPE
# API_KEY secret
シークレットを削除するには wrangler secret delete を使います。
wrangler secret delete API_KEY
登録したシークレットも、コード側では通常の env.API_KEY と同じように取得できます。
export default {
fetch(request: Request, env: Env) {
// [vars] で設定したものも wrangler secret put で登録したものも
// 同じように env から取得できる
const apiKey = env.API_KEY
return new Response('OK')
},
}
原因5: TypeScript で Env インターフェースの型定義がない
TypeScript プロジェクトで env オブジェクトを使う際、型定義がないと型エラーになるか、any になって型の恩恵が受けられません。
// ❌ 型定義なしだと型エラーになる場合がある
export default {
fetch(request: Request, env: unknown) {
const apiKey = env.API_KEY // エラー: Property 'API_KEY' does not exist on type 'unknown'
},
}
wrangler types コマンドを実行すると、wrangler.toml の設定から TypeScript の型定義ファイルを自動生成できます。
wrangler types
# worker-configuration.d.ts が生成される
生成される型定義ファイルの例:
// worker-configuration.d.ts(自動生成)
interface Env {
API_BASE_URL: string
APP_ENV: string
// wrangler secret put で登録したシークレットも含まれる
API_KEY: string
// D1 データベースバインディング
DB: D1Database
// KV 名前空間バインディング
KV: KVNamespace
}
手動で型定義を書く場合は以下のように定義します。
// src/types.ts
export interface Env {
// [vars] セクションの変数
API_BASE_URL: string
APP_ENV: string
// シークレット
API_KEY: string
// サービスバインディング
DB: D1Database
KV: KVNamespace
R2: R2Bucket
}
Cloudflare Pages の場合は設定場所が異なる
Cloudflare Pages を使っている場合、環境変数の設定場所が Workers と異なります。
Workers の場合:
- 平文変数:
wrangler.tomlの[vars] - シークレット:
wrangler secret put
Pages の場合:
- 平文変数・シークレット両方: Cloudflare ダッシュボードの「Workers & Pages」→ プロジェクト選択 →「Settings」→「Environment variables」
Pages Functions(Pages に付属するサーバーサイド関数)で環境変数を使う場合も同様に、env 引数から取得します。
// functions/api/[[path]].ts(Pages Functions)
export const onRequest: PagesFunction<Env> = async (context) => {
const apiKey = context.env.API_KEY // Pages でも同じパターン
return new Response(JSON.stringify({ key: apiKey }))
}
ローカル開発(wrangler pages dev)では、Workers と同様に .dev.vars ファイルが使えます。
ビルド時に環境変数が undefined になる(Workers Builds / Pages のビルド)
結論: ダッシュボードの環境変数は「ランタイム用」と「ビルド用」が別枠です。 npm run build 中に参照する値(SSG で API からデータを取ってくるキー、VITE_* / NEXT_PUBLIC_* のようにビルド時に埋め込まれる値など)は、ランタイム側に登録してもビルドには渡りません。
- Workers(Git 連携の Workers Builds): 「Settings」→「Build」内の Variables and secrets に登録する
- ランタイム(
envで読む値): 「Settings」→ Variables and Secrets に登録する
ビルドログに Missing environment variable のようなエラーが出ているのに、ダッシュボードには値が登録されている、という場合はほぼこのパターンです。値を登録し直したあとは、失敗したデプロイの再実行ではなく新しいコミットを push してビルドし直すと確実です。
まとめ
| 症状 | 原因 | 対処法 |
|---|---|---|
process.env.XXX が undefined |
Workers は Node.js ではない / compatibility_date が 2025-04-01 より前 |
env.XXX(ハンドラ第2引数)を使う。または nodejs_compat + 日付更新 |
ハンドラの外で env が使えない |
env は引数でしか渡ってこない |
import { env } from "cloudflare:workers" |
env.XXX が undefined |
wrangler.toml に [vars] 未設定 |
[vars] セクションに追加する |
ローカルでのみ undefined |
.dev.vars / .env がない、または両方あって .env が無視されている |
どちらか一方に KEY=VALUE で記述 |
本番でのみ undefined |
シークレット未登録 | wrangler secret put で登録する |
ビルド中だけ undefined |
ランタイム用の枠に登録している | ダッシュボードの Build 側 Variables に登録 |
| TypeScript 型エラー | Env インターフェース未定義 |
wrangler types で型ファイルを生成 |
Cloudflare Workers の環境変数は Node.js の process.env とは別の仕組みです。「env オブジェクト(または cloudflare:workers)経由で取得する」「ローカルは .dev.vars か .env のどちらか一方」「本番シークレットは wrangler secret put」という3点を押さえておけば、ほとんどの問題は解決できます。