環境変数って.envに入れれば安全なの? なんとなく使ってるのでちゃんと整理してみた

目次
環境変数って.envに入れれば安全なの?なんとなく使ってるのでちゃんと整理してみた
Web開発をしていると、かなり早い段階で .env が出てきます。
APIキー。 DBの接続情報。 外部サービスのURL。 メール送信の認証情報。
とりあえずコードへ直接書かず、
API_KEY=xxxx
DATABASE_URL=xxxxみたいに .env へ入れる。
自分も普段そうしています。
ただ、あるとき改めて考えると、
「.envに入れたら安全」って、何が安全なんだ?
となりました。
.env は便利です。
でも、秘密情報を入れた瞬間に自動で暗号化される金庫ではありません。
この記事では、
環境変数は何のためにあるのか
.envは何を守ってくれるのか
何を守ってくれないのか
Next.jsではどこまでブラウザに見えるのか
GitHub Actionsや本番環境ではどう扱うのか
を整理します。
まず結論。.envは「秘密を守る仕組み」より「コードと設定を分ける仕組み」
.env を理解するとき、自分の中ではここが一番大事でした。
.env = 秘密の金庫
ではなく、
.env = コードとは別に設定値を置くためのファイル
と考えた方が分かりやすいです。
たとえば、コードへ直接こう書いたとします。
const apiKey = "super-secret-api-key"これをGitへcommitすると、その値はソースコードの履歴へ入ります。
あとから行を消しても、過去のcommitに残る可能性があります。
そこで、
const apiKey = process.env.API_KEYとして、値そのものはコードの外へ出す。
これが環境変数を使う大きな意味です。
開発環境ではローカル用。 ステージングではステージング用。 本番では本番用。
同じコードでも、実行環境ごとに設定値を変えられる。
これがかなり便利です。

.envの中身は普通に文字列。置いただけでは暗号化されない
ここは意外と重要です。
一般的な .env ファイルをテキストエディタで開けば、値はそのまま読めます。
DATABASE_URL=postgres://...
API_KEY=abc123...つまり、
PCへアクセスできる人
ファイルを読めるプロセス
バックアップを取得できる人
などから絶対に見えなくなる仕組みではありません。
だから、
.envに移したからAPIキーは安全
とは言い切れません。
.env が主に防いでくれるのは、
秘密値をソースコードへ直接書いて、Gitで共有してしまう事故
です。
もちろんそれだけでもかなり大きいです。
ただし「ファイルに書いてある秘密を暗号化して守る」のとは別の話です。
まず絶対に避けたいのは.envをGitへ入れること
Next.jsの公式Docsでも、.env ファイルは基本的にGitへcommitしないよう注意されています。
create-next-app のデフォルト構成でも、.env ファイルが .gitignore の対象になるようになっています。
参考: https://nextjs.org/docs/app/guides/environment-variables(外部サイト)
たとえば、
.env
.env.local
.env.*.localのように、秘密値を含むローカルファイルを除外します。
ここで注意したいのは、
Gitへpushする前に消せばOK
ではないことです。
一度commitへ入った秘密値は、Git履歴へ残っている可能性があります。
もし本物のAPIキーやトークンを誤ってcommitしたら、
ファイルを消すだけではなく、キー自体を無効化して新しく発行する
ところまで考えた方が安全です。
秘密が漏れた可能性があるなら、「見えない場所へ移したから元のキーも安全」とは考えない方がいいです。
Next.jsではNEXT_PUBLIC_を付けた時点で「秘密ではない」
Next.jsを使っていると、こんな環境変数があります。
NEXT_PUBLIC_API_URL=https://example.com名前だけ見ると、
.envに書いてあるから外から見えない?
と思いたくなります。
でも NEXT_PUBLIC_ は逆です。
Next.js公式Docsでは、NEXT_PUBLIC_ を付けた環境変数は、ビルド時にブラウザへ配信するJavaScriptへ埋め込まれると説明されています。
参考: https://nextjs.org/docs/app/guides/environment-variables(外部サイト)
つまり、
NEXT_PUBLIC_API_KEY=本当に秘密のキーみたいなことをすると危険です。
NEXT_PUBLIC_ = ブラウザへ渡してよい値
くらいに考えた方が分かりやすいです。
たとえば、
公開してよいAPIのベースURL
公開前提のAnalytics ID
クライアント側で必要な設定値
などです。
一方、
DATABASE_URL=...
PRIVATE_API_KEY=...
SERVICE_ROLE_KEY=...のような秘密値はサーバー側だけで扱います。
Next.jsでは、NEXT_PUBLIC_ を付けていない環境変数は基本的にサーバー側で利用します。
「サーバー側に置いた」だけでも安心しすぎない
ここまでだと、
NEXT_PUBLIC_を付けなければ安全なのか
となります。
これも「絶対安全」とまでは言えません。
サーバー側だけで扱うことで、ブラウザへ直接配信されるリスクは減ります。
でも、
ログに出力する
エラーメッセージへ含める
APIレスポンスへ間違えて含める
Server ComponentからClient Componentへ渡す
デバッグ用の画面へ表示する
などをすれば漏れる可能性があります。
Next.jsのData Securityガイドでも、秘密値をクライアントへ渡さないことや、サーバー専用コードを明確に分離する考え方が説明されています。
参考: https://nextjs.org/docs/app/guides/data-security(外部サイト)
なので、
環境変数にした = どこからも見えない
ではなく、
環境変数にした上で、使う場所も限定する
ところまで必要です。
ローカルの.envとGitHub ActionsのSecretsは役割が違う
開発中は .env.local で十分なことも多いです。
でもCIでGitHub Actionsを動かすと、GitHub上のrunnerには自分のPCの .env.local はありません。
そこで使えるのがGitHub Actions Secretsです。
GitHubでは、
Repository
Environment
Organization
単位でSecretsを管理できます。
参考: https://docs.github.com/en/actions/concepts/security/secrets(外部サイト)
workflow側では、
env:
API_KEY: ${{ secrets.API_KEY }}のようにして使います。
大事なのは、
秘密値そのものをworkflowファイルへ書かない
ことです。
GitHubのSecretsは、workflowで明示的に参照したときに利用できます。
またEnvironment単位でSecretsを分けられるので、
staging用
production用
のように分離できます。
ローカルとCIで同じ「環境変数」という言葉を使いますが、
値を保管する場所は別
です。
本番環境でも.envファイルを置けばいいのか
自前サーバーなら、本番サーバーへ .env を配置する構成もあります。
ただし、その場合は、
ファイル権限
サーバーへ入れる人
バックアップ
デプロイ方法
ログ
秘密値の更新方法
まで自分で管理する必要があります。
一方、Vercelのようなホスティングサービスには環境変数を管理する仕組みがあります。
Vercelでは2026年8月から環境変数が Config / Secret の2種類に整理され、Secretは保存後にメンバーが値を再表示できない用途として案内されています。
参考: https://vercel.com/changelog/environment-variables-now-use-config-and-secret-types(外部サイト)
さらにProduction / Preview / Developmentなど、環境ごとに値を分けられます。
つまり本番では、
秘密値をリポジトリや配布物へ入れず、実行環境側から渡す
という考え方が基本になります。

開発・ステージング・本番で同じキーを使い回さない
これも環境変数を整理するときに大事です。
たとえば外部APIのキーが一つしかなく、
ローカル
ステージング
本番
ですべて同じものを使っていたとします。
すると、ローカルPCで漏れたキーが、そのまま本番権限を持っている可能性があります。
できるサービスなら、
開発用
ステージング用
本番用
で資格情報を分ける方が安全です。
権限も、
とりあえず全部できるAPIキー
より、
この処理だけできるAPIキー
にした方が、漏れたときの影響を減らせます。
前の記事で暗号鍵を調べたときにも感じましたが、
秘密値は「どこに置くか」だけでなく、「何ができる鍵なのか」まで見る
必要があります。
.env.exampleは何のためにある?
チーム開発やGitHubのリポジトリで、
.env.example
を置くことがあります。
たとえば、
DATABASE_URL=
API_KEY=
NEXT_PUBLIC_API_URL=のように、値を空にしてキー名だけ共有するファイルです。
これなら、
このプロジェクトを動かすには、どの環境変数が必要なのか
を共有できます。
実際の秘密値は入れない。
設定項目の設計図だけをGitで管理する。
という役割です。
個人的にはこれがかなり分かりやすいです。
READMEへ大量に書くより、
必要な環境変数そのものが一覧で見える
ので、環境構築時の抜けも減らせます。
環境変数をログへ出すのは地味に怖い
開発中に、
console.log(process.env)みたいなことをしたくなる場面があります。
値がちゃんと入っているか確認したいからです。
でも本番やCIでこれをやると、ログに秘密値を残す可能性があります。
GitHubなどのサービスにはSecretsをマスクする仕組みがありますが、
マスクしてくれるから何を出してもOK
とは考えない方がいいです。
加工した値。 一部だけ切り出した値。 別形式へ変換した値。
などは意図どおり隠れない可能性があります。
確認したいなら、
console.log(Boolean(process.env.API_KEY))のように、
値そのものではなく「存在するか」だけを見る
方が安全です。
秘密情報をデバッグしやすくするために表示した結果、そのログが別の漏えい経路になる。
これは避けたいところです。
環境変数には「秘密」と「ただの設定」が混ざっている
ここも整理するとかなり分かりやすくなりました。
環境変数に入れるものが、全部秘密とは限りません。
公開されても問題ない設定
NEXT_PUBLIC_SITE_URL=https://example.com
NEXT_PUBLIC_ANALYTICS_ID=xxxx外へ出してはいけない秘密
DATABASE_URL=...
PAYMENT_SECRET_KEY=...
PRIVATE_API_KEY=...両方とも環境変数です。
でも扱いはまったく違います。
だから、
環境変数だから安全か?
ではなく、
この値は秘密なのか。秘密なら、どこまで見えていいのか。
を最初に決める方がいいです。
自分なら環境変数をこの4段階で確認する
環境変数を追加するとき、自分なら最低限この4つを確認します。
1. これは公開していい値か
ブラウザから見えても問題ないのか。
秘密なら NEXT_PUBLIC_ のようなクライアント公開の仕組みへ乗せない。
2. Gitへ入っていないか
.env が .gitignore されているか。
過去にcommitしていないか。
3. 環境ごとに分ける必要があるか
local / staging / productionで値や権限を分離できるか。
4. 漏れたときに交換できるか
キーやトークンをローテーションできるか。
どこで使っているか把握できているか。
この4つを見るだけでも、
「.envに入れたからOK」
からかなり先へ進めます。
.envは金庫ではない。でも使う意味はかなり大きい
今回整理してみて、.env の役割はかなりシンプルでした。
コードと設定を分ける。
秘密値をGitへ直接入れない。
環境ごとに違う値を渡せるようにする。
これだけでも十分重要です。
ただし、
.envファイル自体が秘密を暗号化して守ってくれるわけではない。
Next.jsなら、NEXT_PUBLIC_ を付ければブラウザへ出ます。
CIならGitHub Actions Secretsのような仕組みがあります。
ホスティング側にもSecret管理があります。
そして秘密値を使うコード自体も、クライアントへ漏らさないようにしないといけません。
だから自分の中では、
.envは「秘密にする場所」ではなく、「秘密をコードから分離する入口」
くらいに考えるのが一番しっくりきました。
環境変数を追加するときに、
この値は誰から見える?
どこに保存される?
どの環境で使う?
漏れたら交換できる?
まで考える。
そこまでやって初めて、「環境変数として安全に扱う」に近づくと思います。


