← 記事一覧に戻る

Cloudflare Workers で環境変数が undefined になる・読めない時の原因と対策【wrangler.toml / .dev.vars / Pages】

Cloudflare Workers で環境変数が読めなくてハマった

Cloudflare Workers / Pages に移行した直後によくある問題が「環境変数が undefined になる」というものです。Node.js や Vercel に慣れていると、同じ感覚で .env ファイルを作ったり process.env を使ったりしてしまいますが、Cloudflare Workers の実行環境は全く異なります。

この記事では、環境変数が読めない5つの原因と、それぞれの対処法を解説します。

こんな症状が出ていませんか?

  • env.API_KEYundefined になる
  • ({}).MY_VARundefined になる
  • ローカル(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 ファイルが使えます。

まとめ

症状 原因 対処法
({}).XXXundefined Workers は Node.js ではない env.XXX(ハンドラ第2引数)を使う
env.XXXundefined wrangler.toml[vars] 未設定 [vars] セクションに追加する
ローカルでのみ undefined .dev.vars ファイルがない .dev.varsKEY=VALUE で記述
本番でのみ undefined シークレット未登録 wrangler secret put で登録する
TypeScript 型エラー Env インターフェース未定義 wrangler types で型ファイルを生成

Cloudflare Workers の環境変数は Node.js の process.env とは全く別の仕組みです。「env オブジェクト経由で取得する」「ローカルは .dev.vars」「本番シークレットは wrangler secret put」という3点を押さえておけば、ほとんどの問題は解決できます。