サイト運営
SSRホスティングの環境変数 ビルド時とランタイムを取り違えない
Next.jsなどのSSRで、ビルド時注入と実行時のdual、署名鍵の変数名食い違いでセッションだけ死ぬときの切り分け。
SSR(サーバーサイドレンダリング)付きのホスティングでは、環境変数が ビルドのとき と リクエスト処理のとき の二段になることがあります。画面やAPIの一部は動くのに、ログイン直後だけ弾かれる、といった症状の多くはここにあります。Amplify HostingのNext.js SSRや、類似の「ビルド用コンピュート」と「実行用コンピュート」が分かれる構成を想定しています。ホスト固有の画面は変わるので、公式の環境変数の説明もあわせて見てください。
この記事でわかること
- ビルド時注入とランタイム参照の違い
- 署名鍵の 環境変数名の食い違い でセッションだけ死ぬ理由
- 秘密をどこに置くか(環境変数/Secrets Manager)
- 切り分けの順番とチェックリスト
- 作業前にAIへ渡す入力(貼り付け文)
何が二段になっているか
ざっくり次の二層です。
| 層 | いつ効くか | 典型的な使い道 |
|---|---|---|
| ビルド時 | npm run build など | NEXT_PUBLIC_*、ビルドに埋め込まれる定数 |
| ランタイム | 各リクエストのサーバー処理 | Cookie署名、DBテーブル名、API秘密 |
静的エクスポートだけなら「ビルド時に全部決まる」に近づきます。SSRやServer Actions、Route Handlerがあるアプリでは、実行中のプロセスが読む値が別経路で渡っていないと、ビルドは通っても本番だけ挙動が壊れます。
ホスティングによっては、コンソールに入れた環境変数がビルドコンテナには見えるが、SSRランタイムには自動では載らない、またはその逆、ということもあります。ドキュメントの「Build」と「Runtime/SSR」の欄を分けて確認してください。
よくある事故: 署名鍵の名前が違う
Cookieセッションを自前で署名している場合、次のような別名が混在しやすいです。
- 設計書・コンソール:
COOKIE_SIGNING_KEY - コードやローカル例:
SESSION_SECRET - フレームワーク既定:
AUTH_SECRETなど別系統
コードが一方だけを読み、ホスティングにはもう一方だけが入っていると、次が起きます。
1.ヘルスチェックや公開ページは200で見える 2.ログイン処理は「書けたつもり」になることもある 3.直後のリクエストで署名検証に失敗し、セッションだけ無いように見える
「サイト全体が落ちた」のではなく、認証境界だけ死ぬのが特徴です。切り分けでは、デプロイ失敗やDNSより先に、署名に使う変数名がコードとコンソールで一致しているかを見てください。
実装側では、移行期間だけ両方を読む(例: COOKIE_SIGNING_KEY または SESSION_SECRET)のも一つの手です。長期的には 正の名前を一つに決め、設計・コンソール・.env.example・コードを揃えます。
ビルド時に .env へ書き出すパターン
一部の構成では、ビルドスクリプトでホストの環境変数を .env.production などに書き出し、Next.jsのビルドがそのファイルを取り込む、というやり方をします。イメージは次のとおりです(名前は例です)。
# ビルド前: ホストが渡した値のうち、必要なキーだけをファイルへ
env | grep -E '^(COOKIE_SIGNING_KEY|SESSION_SECRET|SESSION_COOKIE_NAME)=' >> .env.production || true
npm run build
注意点は次です。
- grepの許可リストに入れ忘れたキーは、ビルド成果物にもランタイム想定にも載らない
- ビルドログに秘密が流れる設定になっていないか確認する
- ランタイムが
.env.productionではなく、ホストのランタイム環境変数だけを見る場合、この書き出しだけでは足りない
「コンソールには入れたのにアプリが知らない」ときは、ビルド許可リスト・ランタイム設定・コードの process.env.名前 の三点を突き合わせます。
秘密の置き場を分ける
| 種類 | 置き場の目安 |
|---|---|
| Cookie署名鍵・短い設定名 | ホスティングの環境変数(値はログ・Git・チャットに出さない) |
| 決済のSecret/Webhook Signing secret | Secrets Manager等。アプリはランタイムで取得 |
| ブラウザに出してよい公開キー | NEXT_PUBLIC_* など公開用のみ |
決済鍵をビルド時の環境変数だけに頼ると、鍵ローテーションや「ビルド成果物に残る」リスクが増えます。SSRランタイムから秘密ストアを読む方が切り分けやすいです。公開ガイドのStripe編でも、ランタイム取得を前提にしています。
切り分けの順番
- 症状の範囲 … 全ページか、ログイン後だけか
- コードが読む名前 … 署名・Cookie名・テーブル名の
process.envを列挙する - ホストの設定 … ビルド用とランタイム用の両方に、同じ綴りであるか
- 別名の吸収 … コードが別名を許しているなら、コンソール側の名前と突合する
- 値の有無だけ確認 … ヘルスチェックで「セット済み/未セット」を返す(値そのものは返さない)
- ローカル再現 … 本番と同じ変数名だけを入れた
.env.localで、別名をわざと外して再現する
値をIssueやチャットに貼らないでください。入れ直しが必要なら、ホスト側でローテーションし、古い鍵は捨てます。
読者チェックリスト
- 署名鍵の 正の環境変数名 が設計・コード・ホストで一つに決まっている
- ビルド時注入が必要なら、許可リストにその名前が入っている
- ランタイム(SSR)側にも、同じ名前で値が渡る経路がある
- 決済など重い秘密は、環境変数直書きより秘密ストア+ランタイム取得を検討した
- 障害時に「セッションだけ死ぬ」を疑う手順がある
- ヘルスチェックは有無だけ示し、秘密の値を露出しない
作業前にAIへ渡す入力
人間向けの長い手順は書きません。次はすべて、作業前に AIに読ませる/貼る文 です。
AI向け:永続ルール(rules / AGENTS.md)
SSR/ホスティングの環境変数は、ビルド時注入とランタイムを取り違えるな。署名鍵などの名前食い違いを疑え。
プレビューと本番で変数名を揃え、設計か README に一覧する。秘密の値はチャットに出すな。
AI向け:この作業の開始時にチャットへ貼る文
セッションや env 関連の不具合を見る。コードの前に、ビルド時とランタイムの変数名の対応表を出し、食い違い候補を列挙せよ。秘密の値は書くな。
人間向け
なし。上をAIに渡すだけでよい。
関連
- Stripeサンドボックスでサブスク課金まで通す(秘密はランタイムで読む前提)
- Gemini無料キーとGitHub Secretsの設定(Actions側の秘密の置き方)
- 画面に出ないときはデプロイを疑う