Next.js читає .env-файли й робить змінні доступними через process.env. Але де саме вони доступні, залежить від префікса.
Без префікса - лише на сервері:
DATABASE_URL=postgres://...
STRIPE_SECRET_KEY=sk_live_...
Доступні в серверних компонентах, обробниках маршрутів, серверних функціях. У браузері process.env.STRIPE_SECRET_KEY - undefined.
З префіксом NEXT_PUBLIC_ - у браузері теж:
NEXT_PUBLIC_ANALYTICS_ID=G-XXXX
NEXT_PUBLIC_API_URL=https://api.example.com
setupAnalytics(process.env.NEXT_PUBLIC_ANALYTICS_ID);
Як це працює: під час next build Next.js підставляє значення прямо в код JavaScript, що йде в браузер. Тобто:
- значення публічне - будь-хто побачить його в коді сторінки. Сюди можна класти лише те, що й так не секретно;
- значення фіксується на момент збирання. Змінити
NEXT_PUBLIC_API_URLна сервері після збірки - нічого не змінить у вже зібраних файлах. Один Docker-образ для staging і production з різнимиNEXT_PUBLIC_*не працюватиме як очікується; - динамічне звернення не підставляється:
process.env[name]чи деструктуризаціяconst { NEXT_PUBLIC_X } = process.envдадутьundefinedу браузері.
Як передати конфігурацію під час виконання, якщо образ один на всі середовища:
- читати змінні без префікса в серверному компоненті й передавати потрібні значення в клієнтські компоненти як props;
- або ендпойнт конфігурації, з якого клієнт отримує налаштування.
Порядок файлів: .env.local (не в Git, перекриває все), .env.development / .env.production, .env. Змінні оточення процесу мають пріоритет над файлами.
Пастки безпеки:
- випадково додати
NEXT_PUBLIC_до секрету - класичний витік ключа API; - серверний модуль з секретами, імпортований у клієнтський компонент, потрапляє в бандл - захист: пакет
server-onlyу таких модулях дає помилку збірки при імпорті з клієнта.
Аналог у Vite (Laravel-проєкти) - префікс VITE_ і import.meta.env, з тими самими правилами: усе з префіксом публічне й фіксується при збиранні.