環境変数とは?

環境変数は、アプリケーションの実行環境に応じて変わる設定値を外部から注入する仕組みです。APIキー、データベースURL、デバッグフラグなどをコードにハードコードせずに管理できます。

なぜ環境変数が必要か?

理由説明
セキュリティAPIキーやパスワードをコードに書かない
環境切り替え開発/ステージング/本番で異なる設定を使える
チーム開発個人のAPIキーを共有しなくて済む
12-Factor Appモダンなアプリケーション設計の原則

.env ファイルの基本

書き方

# .env
# コメントは # で始める

# データベース
DATABASE_URL=postgresql://user:password@localhost:5432/myapp
DATABASE_POOL_SIZE=10

# API キー
API_KEY=sk-xxxxxxxxxxxxxxxxxxxx
API_SECRET=your-secret-key

# アプリケーション設定
NODE_ENV=development
PORT=3000
DEBUG=true

# 複数行の値はクォートで囲む
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"

# 空白を含む値もクォートで囲む
APP_NAME="My Awesome App"

.env ファイルの種類

.env                 ← デフォルト値(Gitにコミット可能な共通設定)
.env.local           ← ローカル上書き(Gitにコミットしない)
.env.development     ← 開発環境用
.env.staging         ← ステージング環境用
.env.production      ← 本番環境用
.env.test            ← テスト環境用
.env.example         ← テンプレート(Gitにコミットする)

.gitignore の設定

# .gitignore
.env
.env.local
.env.*.local
.env.development
.env.staging
.env.production

# テンプレートはコミットする
!.env.example

.env.example の重要性

新しいメンバーがプロジェクトに参加したとき、どの環境変数が必要かを知るために .env.example を用意します。

# .env.example(Gitにコミットする)
# 実際の値は .env.local に書いてください

# データベース(必須)
DATABASE_URL=postgresql://user:password@localhost:5432/myapp

# API キー(必須: https://dashboard.example.com で取得)
API_KEY=your-api-key-here
API_SECRET=your-api-secret-here

# アプリケーション設定
NODE_ENV=development
PORT=3000
DEBUG=true

フレームワーク別の環境変数

Node.js(dotenv)

npm install dotenv
// app.js
import 'dotenv/config';
// または
import dotenv from 'dotenv';
dotenv.config();

// 使用
const port = process.env.PORT || 3000;
const dbUrl = process.env.DATABASE_URL;

Vite

# .env
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App
SECRET_KEY=this-is-not-exposed  # VITE_ プレフィックスなし → クライアントに公開されない
// クライアントコード
console.log(import.meta.env.VITE_API_URL);
console.log(import.meta.env.MODE);

// SECRET_KEY はアクセスできない(セキュリティ)

Next.js

# .env.local
# NEXT_PUBLIC_ プレフィックスでクライアントに公開
NEXT_PUBLIC_API_URL=https://api.example.com

# プレフィックスなし → サーバーサイドのみ
DATABASE_URL=postgresql://...
SECRET_KEY=xxx
// クライアントコード
const apiUrl = process.env.NEXT_PUBLIC_API_URL;

// サーバーコード(API Routes, Server Components)
const dbUrl = process.env.DATABASE_URL;

Astro

# .env
# PUBLIC_ プレフィックスでクライアントに公開
PUBLIC_API_URL=https://api.example.com

# サーバーサイドのみ
SECRET_KEY=xxx
// クライアント
console.log(import.meta.env.PUBLIC_API_URL);

// サーバー
console.log(import.meta.env.SECRET_KEY);

プレフィックス一覧

フレームワーククライアント公開プレフィックス
ViteVITE_
Next.jsNEXT_PUBLIC_
AstroPUBLIC_
Create React AppREACT_APP_
NuxtNUXT_PUBLIC_

型安全な環境変数

TypeScript + zod でバリデーション

// src/env.ts
import { z } from 'zod';

const envSchema = z.object({
  NODE_ENV: z.enum(['development', 'staging', 'production']).default('development'),
  PORT: z.coerce.number().default(3000),
  DATABASE_URL: z.string().url(),
  API_KEY: z.string().min(1),
  API_SECRET: z.string().min(1),
  DEBUG: z.coerce.boolean().default(false),
});

// アプリ起動時にバリデーション
export const env = envSchema.parse(process.env);

// 型推論が効く
// env.PORT → number
// env.DATABASE_URL → string
// env.DEBUG → boolean
// 使用例
import { env } from './env';

const app = express();
app.listen(env.PORT, () => {
  console.log(`Server running on port ${env.PORT}`);
});

T3 Env(Next.js向け)

// src/env.mjs
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";

export const env = createEnv({
  server: {
    DATABASE_URL: z.string().url(),
    SECRET_KEY: z.string().min(1),
  },
  client: {
    NEXT_PUBLIC_API_URL: z.string().url(),
  },
  runtimeEnv: {
    DATABASE_URL: process.env.DATABASE_URL,
    SECRET_KEY: process.env.SECRET_KEY,
    NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
  },
});

CI/CD での環境変数

GitHub Actions

# .github/workflows/deploy.yml
jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      NODE_ENV: production

    steps:
      - uses: actions/checkout@v4

      - name: Build
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          API_KEY: ${{ secrets.API_KEY }}
        run: npm run build

      - name: Deploy
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
        run: ./deploy.sh

Cloudflare Pages / Workers

# Wrangler で設定
wrangler secret put API_KEY
# プロンプトで値を入力

# wrangler.toml(シークレットでない値)
[vars]
ENVIRONMENT = "production"

シークレット管理

避けるべきこと

# NG: シークレットをコードにハードコード
const API_KEY = "sk-1234567890abcdef";

# NG: シークレットをコミット
git add .env
git commit -m "add env file"

# NG: シークレットをログに出力
console.log("API Key:", process.env.API_KEY);

シークレット管理ツール

ツール特徴
GitHub SecretsGitHub Actions用、無料
AWS Secrets ManagerAWS統合、自動ローテーション
HashiCorp Vaultオンプレ/クラウド対応、高機能
Dopplerチーム開発向け、UIが便利
1Passwordチームシークレット管理
Infisicalオープンソース

git-secrets で誤コミットを防ぐ

# インストール
brew install git-secrets

# リポジトリに設定
cd your-repo
git secrets --install
git secrets --register-aws  # AWSキーのパターンを登録

# カスタムパターンを追加
git secrets --add 'sk-[a-zA-Z0-9]{32,}'  # OpenAI APIキーのパターン

# コミット前にチェックされる
git commit -m "feat: add new feature"
# → シークレットが含まれていたらコミット拒否

環境変数が漏洩した場合

即座にやるべきこと

  1. 該当のキーを無効化/ローテーション
  2. GitHubの場合: git filter-branch や BFG Repo-Cleaner で履歴から削除
  3. 監査ログの確認: 不正利用の痕跡を確認
  4. 影響範囲の特定: どのサービスに影響があるか
# BFG Repo-Cleaner でシークレットを履歴から削除
java -jar bfg.jar --replace-text passwords.txt repo.git

# passwords.txt の内容
sk-1234567890abcdef==>***REMOVED***

ベストプラクティスまとめ

プラクティス説明
.env.gitignore に追加シークレットをコミットしない
.env.example を用意チームメンバーが必要な変数を知れる
型バリデーションzod等で起動時にチェック
プレフィックスルールクライアントに公開する変数を明確に
シークレット管理ツール本番環境は専用ツールを使う
ローテーション定期的にキーを更新
最小権限必要最小限の権限のキーを使う
ログに出さないシークレットをログに出力しない

まとめ

環境変数の管理は、アプリケーションのセキュリティと運用の基盤です。

  1. 開発環境: .env + .env.local で管理
  2. CI/CD: GitHub Secrets 等を使用
  3. 本番環境: シークレット管理ツールを使用
  4. 型安全: zod でバリデーション

「シークレットをコードに書かない」を徹底しましょう。


環境変数に含まれるBase64エンコードされた値のデコードには、AssistyのBase64変換ツールが便利です。