.envファイル徹底解説:解析ルール・JSON相互変換・設定管理
.envファイルとは、KEY=VALUEのペアを並べたプレーンテキストのリストであり、設定や秘密情報をソースコードの外に切り出しておくためのものだ。Node、Vite、Next.js、Python、Ruby、Docker Compose がいずれもこの形式を読み込み、プロセスの環境変数として展開する。env to json で検索しているなら、目的はたいてい二つのどちらかだ。.envをツール向けの構造化された JSON に変換したいか、あるいはルールを十分に理解して安全に扱えるようになりたいか、である。
つまずきやすい点が三つあるので、まずそこから片づけておこう。
.envファイルはフラットである。 入れ子はない。すべてのキーは最上位に並ぶ。- 値はすべて文字列である。 dotenv は型変換を一切行わない。
PORT=8080は8080ではなく"8080"として読み込まれる。 - 文法は非公式である。 正式な仕様が存在しないため、引用符・コメント・エスケープといった細部の扱いはローダーごとに食い違う。
この記事で扱うのは、dotenv の解析ルール、.env↔JSON のマッピング(とどちらの方向にも変換する理由)、.envと JSON のどちらを使うべきかの判断基準、そして本番投入前に設定を検証する方法だ。ここで説明する処理は .env→JSON 変換ツール がすべてブラウザー内で完結させるので、本物の認証情報が詰まった.envであってもページの外には出ない。
.envファイルとは何か
.envファイルは、環境設定の事実上の標準だ。dotenv ライブラリ(およびほぼすべての言語への移植版)がこのファイルを読み、各ペアを実行中のプロセスに注入する。アプリ側は接続文字列をハードコードする代わりに process.env.DATABASE_URL を読むことになる。このファイルはデータベースのパスワード、API キー、OAuth の秘密情報、アクセストークンを保持するため、ほぼ必ず git の管理対象から除外され、機密として扱われる。
1行の構造
意味を持つ各行は KEY=VALUE のペアで、最初の = 記号で分割される。コメント行と空行はスキップされ、先頭に付くことのある export は取り除かれる。
# Database — this whole line is a comment
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
DATABASE_POOL_SIZE=10
# A blank line above is ignored
export DEPLOY_ENV=production # the `export` prefix is removed
JWT_SECRET="super secret value"
キーは最初の = より前のすべてで、前後の空白は除去される。export という接頭辞が存在するのは、このファイルをシェルでそのまま source できるようにするためで、dotenv ローダーは自動的にこれを取り除く。最初の = で分割する点が重要なのは、DATABASE_URL のような値がクエリ文字列の中に自前の = を含むことがよくあるからだ。
なぜ設定を環境に置くのか
その根拠は Twelve-Factor App にある。そこでは設定を環境に格納せよと述べられている。設定はデプロイごとに変わる(dev、staging、production)が、コードは変わらない。設定を環境に置いておけば、ソースを編集したり再デプロイしたりせずにデータベースのホストを切り替えられ、同じビルドがどこでも動く。
よくある誤読がある。「設定をファイルに置くな」という言葉を引いて.envは禁止だと結論づける人がいる。だが本来のルールはもっと限定的だ。避けるべきなのは、設定をアプリの中にコミットされたファイルにすること、つまりコードと混ぜてバージョン管理に追跡させることである。開発用にローカルで git 管理から外した.envを使うのは問題なく、むしろ標準的だ。やってはいけないのは、秘密情報を埋め込んだ本物の.envを本番に持ち込むことだ。
.envの解析ルール(ツールによって解釈が分かれるエッジケース)
.envファイルを解析するための正式な仕様が存在しないため、すべてのローダーが境界部分で独自の判断を下す。以下のルールは広く採用されている dotenv の慣習であり、本コンバーターが実装しているものであり、ほとんどのランタイムが合意しているものだ。
引用符とエスケープ
値をどう引用符で囲むかで、すべてが変わる。
- 二重引用符はエスケープシーケンスを処理する。
\nは改行に、\tはタブに、\rはキャリッジリターンに、\\はバックスラッシュに、\"はリテラルの二重引用符になる。二重引用符で囲んだ値は閉じ引用符まで複数行にまたがることもでき、これが PEM 秘密鍵を.envに収める方法だ。 - 単一引用符はリテラルだ。シェルとまったく同じく、エスケープ処理は一切起こらない。
'no \n escapes here'はバックスラッシュとnをそのまま残す。 - 引用符なしの値は行末まで続き、末尾の空白は除去される。インラインの
#(スペースに続くハッシュ)はコメントの開始で、そこから先は取り除かれる。
この最後のルールは、16 進カラーコードで人をはまらせる。COLOR=#ff0000 は # 以降がすべて失われる。引用符で囲めば(COLOR="#ff0000")値は無事に残る。
値はすべて文字列である
これはdotenv形式について最も重要な事実だ。PORT=8080 は数値の 8080 としては読み込まれない。process.env の値は実行時に常に文字列なので、"8080" という文字列として読み込まれる。dotenv は型変換を一切行わない。
これは実際にバグを生む。if (process.env.DEBUG) は DEBUG=false であっても真と評価される。"false" が空でない文字列だからだ。数値比較は静かに失敗する。"8080" は 8080 ではないからだ。あらゆる「型を推論する」機能(本コンバーターのトグルを含む)は、dotenv の上に追加された利便性のための層であって、標準の一部ではない。これを使うときは意図的に使い、JSON が実際にアプリへ渡る値とは異なるものになると理解したうえで使うこと。
重複キー、複数行の値、変数展開
同じキーが二度現れたときは、最後の出現が勝つ。先に書かれた値は黙って捨てられる。これは設定ミスの定番の罠だ。長いファイルの末尾近くにまぎれ込んだ重複が、本来使いたかった値を静かに覆い隠してしまう。よくできたコンバーターは、重複を握りつぶさずに警告で知らせる。
複数行の値が機能するのは二重引用符の内側だけで、閉じ " まで複数行にわたって囲まれる。そして ${VAR} による変数展開(ある変数を別の変数から参照すること)は一部のローダーには存在するが、普遍的ではない。ランタイムをまたいでこれに頼ってはいけない。あるスタックでは問題なく展開されるファイルでも、別のスタックではリテラルの ${VAR} 文字列として読み込まれることがある。
解析ルール早見表
| 入力行 | 解析後の値 | ルール |
|---|---|---|
PORT=8080 | "8080" | 引用符なし、文字列のまま保持(型変換なし) |
APP_NAME=My App # title | "My App" | 引用符なし:インラインの # コメントを除去し、末尾の空白を除去 |
GREETING="Hello,\nWorld" | Hello,⏎World | 二重引用符は \n を実際の改行として処理 |
LITERAL='no \n escapes' | no \n escapes | 単一引用符はリテラル、エスケープ処理なし |
COLOR=#ff0000 | "" | 引用符なしの # はコメント開始。値が失われるので引用符で囲むこと |
export AWS_REGION=us-east-1 | us-east-1 | export 接頭辞を除去 |
EMPTY= | "" | 空の値は有効な空文字列 |
.env vs JSON 設定:どちらをいつ使うか
率直な答えは「JSON のほうが優れている」ではない。両者は別々の問題を解いている。.envファイルはフラットで、文字列のみで、コメントを書けて、環境ごとに分かれる。秘密情報とデプロイ時の上書きのために作られた形式だ。JSON は入れ子にでき、型を持ち、構造化されているが、コメントを書けず、うっかりコミットしやすい。env vs json config の判断は、この使い分けに尽きる。
.envにできないこと
.envは入れ子にできず、配列を持てず、本物の型を載せられない。文字列のフラットなリストだ。設定が本来グループ化される性質のものなら、入れ子にする代わりに接頭辞の規約でフラットに展開する。db オブジェクトではなく DB_HOST と DB_PORT にするわけだ。キーはフラットなまま保ち、グループ化はコード側で組み立て直す。
JSONが得意なこと
構造そのものが目的になるとき、JSON が勝つ。入れ子のオブジェクト、配列、本物の数値・真偽値・null だ。スキーマに照らして検証する形式であり、型を生成する元になる形式でもある。フラットなファイルでは表現できない階層が必要なら、JSON(あるいは下で扱う YAML)が適したツールだ。
判断マトリクス
| 必要なもの | .env | JSON | 理由 |
|---|---|---|---|
| 秘密情報/認証情報 | ✅ | ⚠️ | .envは慣習として git 管理から外す。JSON 設定はうっかりコミットしやすい |
| 環境ごとの上書き | ✅ | ⚠️ | 環境ごとに 1 つの.envが標準的なデプロイパターン |
| 入れ子構造 | ❌ | ✅ | .envはフラット。JSON はネイティブに入れ子にできる |
| 型付きの値(数値/真偽値/null) | ❌ | ✅ | .envの値は常に文字列。JSON は本物の型を持つ |
| インラインコメント | ✅ | ❌ | .envは # をサポート。JSON にはコメント構文がない |
| スキーマ検証 | ⚠️ | ✅ | .env→JSON に変換してから検証。JSON は直接検証できる |
.envとJSONを相互変換する
ルールさえ分かれば、どちらの方向の変換も機械的だ。マッピングは最上位レベルで 1 対 1(各 KEY=VALUE 行が 1 つの JSON プロパティ)であり、唯一込み入っているのは型と入れ子の扱いだけだ。
.env → JSON
各 KEY=VALUE ペアが 1 つの JSON プロパティになる。デフォルトでは値はすべて文字列で、dotenv が実行時に読み込むものに忠実だ。任意の型推論トグルを使うと、引用符なしの数値・真偽値・null が昇格される。結果はフラットなオブジェクトになる。これを行うのは、設定を JSON 専用のツールに流し込む、秘密情報マネージャーへ一括インポートする、スキーマに照らして検証する、あるいは肥大化した.envを構造化データとして読みたい、といった目的のためだ。.env→JSON 変換ツール はまさにこれをブラウザー内で行い、重複キーを見つけたときには警告を出す。
JSON → .env
逆方向はオブジェクトしか受け付けない。最上位が配列や裸のスカラーだと、変数に対応づけるキー名がないからだ。数値と真偽値は裸で書き出され(PORT=8080)、null は空の KEY= になり、スペース・#・改行・引用符を含む文字列はすべて自動的に二重引用符で囲まれエスケープされて、安全に往復できるようになる。入れ子のオブジェクトや配列はフラットなファイルには収まらないため、それぞれが JSON 文字列にシリアライズされ、警告とともに印が付けられる。任意のスイッチで、キーを UPPER_SNAKE_CASE に正規化したり、export 接頭辞を追加したりできる。JSON to .env Converter がこれらすべてを処理する。
往復の安全性と入れ子に関する注意
自動引用符付けが存在するのは、.env → JSON → .env を経ても値が変わらず生き残るようにするためだ。以下が実行可能なコードとしての往復で、コンバーターの挙動と一致する。PORT がこのサイクルを通じて文字列のまま保たれる点に注目してほしい。dotenv が読み込むのとまさに同じ振る舞いだ。
import { parse } from 'dotenv';
// 1. Start with a .env file as text
const envText = `DATABASE_URL=postgres://user:pass@localhost:5432/mydb
PORT=8080
GREETING="Hello, World"
NOTE="value with # hash"`;
// 2. .env -> JSON (dotenv.parse returns string values only)
const config = parse(envText);
console.log(JSON.stringify(config, null, 2));
// {
// "DATABASE_URL": "postgres://user:pass@localhost:5432/mydb",
// "PORT": "8080",
// "GREETING": "Hello, World",
// "NOTE": "value with # hash"
// }
// 3. JSON -> .env (quote only strings that need it)
const needsQuotes = (s) => /[\s#"'\n]/.test(s);
const env = Object.entries(config)
.map(([key, value]) =>
needsQuotes(value) ? `${key}=${JSON.stringify(value)}` : `${key}=${value}`
)
.join('\n');
console.log(env);
// DATABASE_URL=postgres://user:pass@localhost:5432/mydb
// PORT=8080
// GREETING="Hello, World"
// NOTE="value with # hash"
落とし穴は入れ子だ。フラットな設定なら往復は無損失だが、深く入れ子になった構造は不透明な JSON 文字列としてしか.envを通れない。構造が返ってくることを期待するアプリにとっては、読み取れないものになる。設定が本当に階層的なら、代わりに YAML に手を伸ばそう。YAML to JSON 変換ツール と YAMLのNorway問題 のガイドが、その経路とそれ自身の鋭いエッジを扱っている。
環境設定を検証する
設定変数が欠けていたり不正だったりするせいで、本番の午前 3 時に undefined is not a function として表面化する、などということはあってはならない。Twelve-Factor の考え方は早く失敗することだ。デプロイの後ではなく前に設定をチェックする。.envを JSON に変換すれば、それが実用的になる。生の環境変数にはない成熟した検証ツールが JSON には揃っているからだ。
CIでスキーマ検証する
.env → JSON に変換し、その JSON を、必須キー・許可される列挙値・値のフォーマットを宣言したスキーマに照らして検証する。設定ミスのある環境(DATABASE_URL の欠落、不正な LOG_LEVEL、数値でないポート)は、デプロイではなく CI のチェックで失敗する。JSON Schema バリデーション完全ガイド がスキーマの書き方を順を追って説明し、JSON Schema バリデーター がそれをブラウザー内で実行する。
型付き設定
存在チェックの先では、process.env.PORT がコードベース中に散らばる型なしの文字列にならないように、型付きの設定オブジェクトを導き出せる。Zod のようなランタイムスキーマライブラリで起動時に検証して型変換するか、JSON から TypeScript のインターフェースを生成してそれを通じて設定を読む。JSONからTypeScriptへ と JSON to TypeScript 変換ツール が生成のステップを扱う。構造の崩れを早めに見つけられるよう、先に JSON整形ツール で JSON を整形するか、ざっと中身を確かめておくとよい。
秘密情報の取り扱い:.envを安全に扱う
.envは、機能的には認証情報のリストだ。そのものとして扱うこと。
.envは決してコミットしない。 .gitignore に追加する。すべてのキーを空またはプレースホルダーの値とともに列挙した .env.example をコミットする。本物の接続文字列ではなく DATABASE_URL= のように書くのだ。そのファイルはチームの契約だ。どの変数を新しいクローンが必要とするかを、どれも漏らすことなく文書化する。
.envはローカルと開発用。本番は秘密情報マネージャーを使う。 Vault、Doppler、AWS Secrets Manager といったツールは、デプロイ時に秘密情報を環境に注入する。本物の秘密情報を載せた.envを本番ホストに持ち込んではいけない。代わりにマネージャーから取得することで、漏れたファイルや設定を誤ったコンテナーがあなたのキーを引き渡してしまわないようにする。
秘密情報の変換は、ブラウザー内だけで動くツールでしか行わない。 本物の.envをサーバーサイドのコンバーターに貼り付けると、あなたの認証情報がネットワークを越えて他人のマシンへ送られる。ここにある両方のコンバーターはどちらも完全にブラウザー内で動く。DevTools の Network タブを開いて、貼り付けてもリクエストがゼロであることを確認してほしい。これが、サニタイズしたサンプルではなく本番の.envを変換しても安全だといえる差だ。
FAQ
.envファイルをJSONに変換するには?
ファイルを .env→JSON 変換ツール に貼り付けると、ブラウザー内で即座に JSON に解析される。各 KEY=VALUE 行がプロパティになる。値はデフォルトで文字列(dotenv と一致)で、数値や真偽値が欲しければ型推論をトグルする。何もアップロードされないので、本物の秘密情報は自分の端末にとどまる。
.envの値は数値や真偽値か、それとも文字列か?
常に文字列だ。dotenv は型変換を一切行わない。実行時にはすべての process.env の値が文字列なので、PORT=8080 は "8080" であり、DEBUG=false は文字列の "false"(これは真と評価される)だ。あらゆる「型を推論する」オプションは標準の上に追加された利便性の層であって、dotenv そのものの一部ではない。
.envファイルとJSON設定ファイルの違いは?
.envはフラットで、文字列のみで、コメントを書けて、秘密情報と環境ごとの上書きのために作られている。JSON は入れ子で型付きで、本物の数値・真偽値・null を持ち、スキーマに照らして検証できる。ただしコメントがなく、うっかりコミットしやすい。秘密情報には.envを、構造化された設定には JSON を使おう。
.envファイルに入れ子やグループ化した値を持てるか?
持てない。.envは KEY=VALUE ペアのフラットなリストで、入れ子も配列もない。グループ化を表現するには、接頭辞の規約でフラットに展開する(db オブジェクトではなく DB_HOST と DB_PORT)。そして構造はコード側で組み立て直す。本当に階層が必要なら、JSON か YAML を使う。
.envで引用符・#・複数行の値はどう扱われるか?
二重引用符はエスケープ(\n、\t、\\、\")を処理し、閉じ引用符まで複数行にまたがれる。単一引用符はリテラルでエスケープなし。引用符なしの値は行末まで続き、末尾の空白を除去し、スペース+# をインラインコメントとして扱う。だから正当に # を含む値はすべて引用符で囲むこと。
.envファイルをGitにコミットすべきか?
いいえ。.envを .gitignore に追加し、代わりにキーを空の値とともに列挙した .env.example をコミットする。本物のファイルはデータベースのパスワード、API キー、トークンを保持する。これをコミットすると認証情報が履歴に漏れ、ファイルを削除した後でもそこに残り続ける。