Cloudflare Workers で環境変数が読めなくてハマった
Cloudflare Workers / Pages に移行した直後によくある問題が「環境変数が undefined になる」というものです。Node.js や Vercel に慣れていると、同じ感覚で .env ファイルを作ったり process.env を使ったりしてしまいますが、Cloudflare Workers の実行環境は全く異なります。
この記事では、環境変数が読めない5つの原因と、それぞれの対処法を解説します。
こんな症状が出ていませんか?
env.API_KEYがundefinedになる({}).MY_VARがundefinedになる- ローカル(
wrangler dev)では動くが、本番 Worker ではundefinedになる .envファイルに書いたのに全く反映されない- TypeScript で
envオブジェクトの型エラーが出る
原因1: process.env は Cloudflare Workers では使えない
これが最もよくある原因です。 Node.js に慣れているエンジニアが最初にハマるポイントです。
Cloudflare Workers の実行環境は Node.js ではなく、V8 Isolates という独自ランタイムです。そのため process オブジェクト自体が存在せず、process.env を参照すると undefined になります(エラーにならないため気づきにくい)。
// ❌ Node.js の感覚で書くと undefined になる
export default {
fetch(request: Request) {
const apiKey = ({}).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)
})
原因2: wrangler.toml にバインディングが設定されていない
env オブジェクトから変数を取得するためには、wrangler.toml にバインディングとして登録する必要があります。.env ファイルは Workers では自動読み込みされません。
# 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"
よくあるミスとして、変数名のタイポや、[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: ローカル開発に .dev.vars が必要
wrangler dev でローカル開発する際、シークレット(API キーなど機密情報)は .dev.vars ファイルに書く必要があります。.env ファイルは wrangler では読み込まれません。
# ❌ wrangler dev では読み込まれないファイル
.env
.env.local
# ✅ wrangler dev で読み込まれるファイル
.dev.vars
.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 はシークレットを含むため、Git に含めてはいけません。
# .gitignore に追加
echo ".dev.vars" >> .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 ファイルが使えます。
まとめ
| 症状 | 原因 | 対処法 |
|---|---|---|
({}).XXX が undefined |
Workers は Node.js ではない | env.XXX(ハンドラ第2引数)を使う |
env.XXX が undefined |
wrangler.toml に [vars] 未設定 |
[vars] セクションに追加する |
ローカルでのみ undefined |
.dev.vars ファイルがない |
.dev.vars に KEY=VALUE で記述 |
本番でのみ undefined |
シークレット未登録 | wrangler secret put で登録する |
| TypeScript 型エラー | Env インターフェース未定義 |
wrangler types で型ファイルを生成 |
Cloudflare Workers の環境変数は Node.js の process.env とは全く別の仕組みです。「env オブジェクト経由で取得する」「ローカルは .dev.vars」「本番シークレットは wrangler secret put」という3点を押さえておけば、ほとんどの問題は解決できます。
