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 側の問題、と切り分けるのが最短です。

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

原因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 の場合:

Pages の場合:

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_* のようにビルド時に埋め込まれる値など)は、ランタイム側に登録してもビルドには渡りません。

ビルドログに 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点を押さえておけば、ほとんどの問題は解決できます。

技術ブログの記事一覧へ