環境変数とは?
環境変数は、アプリケーションの実行環境に応じて変わる設定値を外部から注入する仕組みです。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);
プレフィックス一覧
| フレームワーク | クライアント公開プレフィックス |
|---|---|
| Vite | VITE_ |
| Next.js | NEXT_PUBLIC_ |
| Astro | PUBLIC_ |
| Create React App | REACT_APP_ |
| Nuxt | NUXT_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 Secrets | GitHub Actions用、無料 |
| AWS Secrets Manager | AWS統合、自動ローテーション |
| 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"
# → シークレットが含まれていたらコミット拒否
環境変数が漏洩した場合
即座にやるべきこと
- 該当のキーを無効化/ローテーション
- GitHubの場合:
git filter-branchや BFG Repo-Cleaner で履歴から削除 - 監査ログの確認: 不正利用の痕跡を確認
- 影響範囲の特定: どのサービスに影響があるか
# BFG Repo-Cleaner でシークレットを履歴から削除
java -jar bfg.jar --replace-text passwords.txt repo.git
# passwords.txt の内容
sk-1234567890abcdef==>***REMOVED***
ベストプラクティスまとめ
| プラクティス | 説明 |
|---|---|
.env を .gitignore に追加 | シークレットをコミットしない |
.env.example を用意 | チームメンバーが必要な変数を知れる |
| 型バリデーション | zod等で起動時にチェック |
| プレフィックスルール | クライアントに公開する変数を明確に |
| シークレット管理ツール | 本番環境は専用ツールを使う |
| ローテーション | 定期的にキーを更新 |
| 最小権限 | 必要最小限の権限のキーを使う |
| ログに出さない | シークレットをログに出力しない |
まとめ
環境変数の管理は、アプリケーションのセキュリティと運用の基盤です。
- 開発環境:
.env+.env.localで管理 - CI/CD: GitHub Secrets 等を使用
- 本番環境: シークレット管理ツールを使用
- 型安全: zod でバリデーション
「シークレットをコードに書かない」を徹底しましょう。
環境変数に含まれるBase64エンコードされた値のデコードには、AssistyのBase64変換ツールが便利です。