◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●SDK 58 ISSUE — Android ウィジェットが定期更新されない、Stripe が静的リンクになるなど初期報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●SDK 58 ISSUE — Android ウィジェットが定期更新されない、Stripe が静的リンクになるなど初期報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/アプリ開発
◇ アプリ開発/2026-10-22中級

SDK 58 から .env.staging は読み込まれません。手元の .env 系ファイルを、70行ほどのスクリプトで棚卸しします

Expo SDK 58 以降、expo のコマンドは .env.staging などの独自ファイルを読み込まず、NODE_ENV の指定も上書きされます。読み込まれるファイルと優先順位を点検するスクリプトを、サンプルで動かして確かめた記録です。

Expo SDK 584環境変数11.envEAS11点検スクリプト

SDK 58 の「Environment variables in Expo」を読み直していて、一文で手が止まりました。

SDK 58 以降、Expo のコマンドは .env.staging や .env.test など、標準以外の .env ファイルを読み込みません。

ステージング用の値を .env.staging に分けて、NODE_ENV=staging を付けて切り替えます。個人開発のアプリで、配信先の URL を環境ごとに分けたくなったとき、私も真っ先に思いつく組み立てです。この一文が正しければ、アップグレードしたあとも、画面は何事もなく動きます。ただし、指している API は、思っていたものとは別かもしれません。

そこで、手元の .env 系ファイルを一度に点検するスクリプトを書き、サンプルプロジェクトで走らせました。公式ドキュメント(更新日 2026-10-12)の記述を、機械的に当てはめるだけの小さな道具です。

公式ドキュメントが決めている三つの規則

ドキュメントから、点検に必要な規則だけを抜き出します。

  1. モード(開発か本番か)ごとに読み込まれるファイルが決まっており、左にあるものが優先されます。
  2. SDK 58 以降、上の表に載らないファイル(.env.staging、.env.test など)は読み込まれません。
  3. SDK 58 以降、各コマンドが NODE_ENV を自分のモードに合わせて設定し、外から渡した値は置き換えられます。たとえば NODE_ENV=production npx expo start は、開発モードで動き、.env.development を読みます。
モード読み込まれるファイル(優先順)
開発.env.development.local → .env.local → .env.development → .env
本番.env.production.local → .env.local → .env.production → .env

三つ目の規則は、eas update にも及びます。eas update は内部で npx expo export を呼ぶため、NODE_ENV=test eas update も、test ではなく production で動くと書かれています。

点検スクリプト

私はアップグレードの前に、まず「何が読まれなくなるか」を機械に数えさせる派です。やることは四つです。.env 系ファイルが開発・本番のどちらで読まれるかを判定します。本番モードで同じキーが複数あるときは、どのファイルの値が勝つかを出します。package.json の scripts では、expo や eas update と並んだ NODE_ENV= がないかを探します。最後に、ソース中の process.env['…'] と const { … } = process.env を探します。ドキュメントによれば、変数は process.env.EXPO_PUBLIC_X のように静的に書かれていないと、バンドルに埋め込まれません。

// env-audit.mjs — Expo SDK 58 の .env 読み込み規則で、プロジェクトの .env 系ファイルと参照箇所を点検します
// 使い方: node env-audit.mjs <プロジェクトのルート>
import { readdirSync, readFileSync, statSync } from 'node:fs';
import { join, extname } from 'node:path';
 
const root = process.argv[2] ?? '.';
 
// 公式ドキュメント(Environment variables in Expo)の表。左ほど優先
const LOADED = {
  development: ['.env.development.local', '.env.local', '.env.development', '.env'],
  production: ['.env.production.local', '.env.local', '.env.production', '.env'],
};
 
const envFiles = readdirSync(root).filter((f) => /^\.env(\..+)?$/.test(f));
console.log('== .env 系ファイル ==');
for (const f of envFiles) {
  const dev = LOADED.development.includes(f);
  const prod = LOADED.production.includes(f);
  const verdict = dev || prod ? `読まれる(開発:${dev ? 'はい' : 'いいえ'} / 本番:${prod ? 'はい' : 'いいえ'})` : '読まれません(SDK 58 以降)';
  console.log(`${f.padEnd(26)} ${verdict}`);
}
 
// 同じキーが複数ファイルにあるとき、本番モードでどれが勝つか
const parse = (f) =>
  Object.fromEntries(
    readFileSync(join(root, f), 'utf8')
      .split('\n')
      .map((l) => l.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)$/))
      .filter(Boolean)
      .map((m) => [m[1], m[2].trim()]),
  );
console.log('\n== 本番モードで実際に効く値 ==');
const winner = {};
for (const f of [...LOADED.production].reverse()) {
  if (!envFiles.includes(f)) continue;
  for (const [k, v] of Object.entries(parse(f))) winner[k] = { v, f };
}
for (const [k, { v, f }] of Object.entries(winner)) console.log(`${k} = ${v}   ← ${f}`);
 
// package.json の NODE_ENV= は SDK 58 以降、expo コマンドに上書きされます
console.log('\n== package.json の NODE_ENV 指定 ==');
const scripts = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).scripts ?? {};
let hit = false;
for (const [name, cmd] of Object.entries(scripts)) {
  if (/NODE_ENV=/.test(cmd) && /\bexpo\b|eas update/.test(cmd)) {
    hit = true;
    console.log(`scripts.${name}: ${cmd}  ← 指定した NODE_ENV は使われません`);
  }
}
if (!hit) console.log('なし');
 
// インライン化されない書き方
console.log('\n== インライン化されない process.env の書き方 ==');
const walk = (d) =>
  readdirSync(d).flatMap((n) => {
    if (n === 'node_modules' || n.startsWith('.')) return [];
    const p = join(d, n);
    return statSync(p).isDirectory() ? walk(p) : ['.ts', '.tsx', '.js', '.jsx'].includes(extname(p)) ? [p] : [];
  });
let bad = 0;
for (const p of walk(root)) {
  readFileSync(p, 'utf8').split('\n').forEach((line, i) => {
    if (/process\.env\[/.test(line) || /\}\s*=\s*process\.env\s*;?\s*$/.test(line)) {
      bad++;
      console.log(`${p.replace(root + '/', '')}:${i + 1}  ${line.trim()}`);
    }
  });
}
if (!bad) console.log('なし');
process.exitCode = envFiles.some((f) => !LOADED.development.includes(f) && !LOADED.production.includes(f)) || bad || hit ? 1 : 0;

依存は Node だけです。node env-audit.mjs ./my-app のように、プロジェクトのルートを渡します。問題が見つかると終了コード 1 を返すので、CI に載せることもできます。

確認用のサンプルで走らせた結果

次の構成のサンプルを作りました。.env、.env.local、.env.production、.env.staging の四つを置き、.env.local には開発機の LAN アドレスを入れています。package.json には NODE_ENV=staging eas update --branch preview を、ソースには三通りの process.env の書き方を一つずつ置きました。

== .env 系ファイル ==
.env                       読まれる(開発:はい / 本番:はい)
.env.local                 読まれる(開発:はい / 本番:はい)
.env.production            読まれる(開発:いいえ / 本番:はい)
.env.staging               読まれません(SDK 58 以降)
 
== 本番モードで実際に効く値 ==
EXPO_PUBLIC_API_URL = http://192.168.0.12:3000   ← .env.local
EXPO_PUBLIC_FLAG = off   ← .env
 
== package.json の NODE_ENV 指定 ==
scripts.build:stg: NODE_ENV=staging eas update --branch preview  ← 指定した NODE_ENV は使われません
 
== インライン化されない process.env の書き方 ==
app/api.ts:1  const { EXPO_PUBLIC_API_URL } = process.env;
app/api.ts:2  export const url = process.env['EXPO_PUBLIC_API_URL'];

見てほしい箇所は二つです。

一つ目は、.env.staging の行です。ファイルはそこにあって、中身も正しいのに、どのモードでも読まれません。エラーは出ません。値が undefined になるか、別のファイルの値に化けるだけなので、気づくのが遅れます。

二つ目は、本番モードの結果です。EXPO_PUBLIC_API_URL に勝っているのは、.env.production ではなく .env.local でした。上の表のとおり、.env.local は本番モードでも読まれ、.env.production より前に並んでいます。開発機のアドレスを書いた .env.local が残っていると、手元で npx expo export や eas update を走らせたとき、本番の値を押しのけます。

ファイルが存在することと、その値が使われることは別の話です。 点検は「どのファイルがあるか」ではなく、「本番モードで、どの行が勝つか」まで確かめます。

直す順番

私なら、次の順に手を入れます。

  1. .env.staging の中身を、読み込まれる場所へ移します。ドキュメントが勧めているのは、EAS を使っているなら eas env:pull です。選んだ環境の値で .env.local を置き換えてくれるので、NODE_ENV に頼らずに済みます。EAS を使わないときは、.env.local や .env を書き換える小さなスクリプトを自作するやり方が紹介されています。
  2. package.json の NODE_ENV=staging を消します。残しても害はありませんが、効いているように見えるぶん、読む人を誤解させます。
  3. .env*.local を .gitignore に入れます。ドキュメントもこの行を例示しています。
  4. process.env['…'] と分割代入を、process.env.EXPO_PUBLIC_X の形に書き直します。

四つ目は、SDK 58 に限った話ではありません。ただ、分割代入は見た目がきれいなので、気づかないまま残りやすい書き方です。

この点検が保証しないこと

確認できたのは、公式ドキュメントの規則をスクリプトが正しく当てはめることまでです。実際の Expo CLI を SDK 58 で走らせて、読み込まれた値を突き合わせたわけではありません。ドキュメントの記述が更新されることもあるので、規則の表は、スクリプトの LOADED を書き換えれば追従できます。

また、EAS のサーバーに登録した環境変数(eas env で管理するもの)は、このスクリプトの対象外です。点検できるのは、リポジトリの中にあるものだけです。

なお、EXPO_PUBLIC_ を付けた値は、ビルドされたアプリの中で平文のまま見えます。鍵のような秘密の値を入れない、という注意も、ドキュメントに明記されています。ソース側の確認は、Rork が書いたコードに鍵が直書きされていないか、40行ほどのスクリプトで一度だけ確かめますに書きました。SDK 58 に上げたあとの、ビルドで増える設定値の確認は、plugins に何も書いていないのに、Info.plist に権限の説明文が 9 件並んでいましたが参考になります。

次にアップグレードの作業をするときは、先にこのスクリプトを走らせて、「読まれないファイル」と「勝っている行」の二つだけ確認してみてください。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

◇ アプリ開発2026-10-21
iOS では動く expo-widgets が Android のビルドだけで落ちる — 版の固定と除外、どちらで逃げるかの判断
expo-widgets を 58.0.17 以降に上げると Android だけ Kotlin のコンパイルで止まる報告があります。症状の見分け方、版の固定と autolinking 除外の使い分け、手元で判定できる確認スクリプトをまとめました。
◇ アプリ開発2026-10-14
App Review で「Guideline 2.1・白い画面」と言われたとき、Expo のリリースビルドで疑う3か所
TestFlight では開くのに、審査では「起動後に白い画面のまま」と差し戻される。Expo + EAS のリリースビルドで、環境変数の environment、secret の可視性、審査員の環境差の3点を順に切り分ける手順を、確認コマンドと診断コードつきでまとめました。
⬡ 開発ツール2026-09-06
eas env:list に --json がないと分かってから、環境変数の棚卸しを組み直しました
eas env:list に --json フラグは用意されていません。--format short の出力を安全に JSON へ変換し、三つの環境の抜けを一行で見つけるまでの手順を書き残します。