Чтобы не сломалось

Как хранить ключи проекта в .env и не отправить их в Git

Для кого

Тому, кто собирает сайт или бота с внешними API

Что вы получите

Проверки файла с ключами до коммита и выкладки

Время чтения

7 мин

Точка А: сейчас

Ключ вставлен в код ради быстрого запуска и попадает в чужие копии проекта

Точка Б

Секрет хранится отдельно, доступен только серверной части и проверен перед публикацией

Рабочий порядок для .env: отделить публичные настройки от секретов, проверить Git, права файла и передачу ключа приложению.

Как хранить ключи проекта в .env, если над сайтом работает ИИ‑агент? Файл удобен, но его имя само по себе не защищает секрет. Нужно решить, какой процесс читает значение, кто может открыть файл, попадёт ли оно в браузер и что окажется в истории Git. Я использую этот порядок в проектах с Next.js, Python-ботами и внешними API. Общий обзор защиты сайта есть в материале о безопасности; здесь только путь одного ключа.

Как разделить публичную настройку и секрет

Адрес открытого сайта, идентификатор счётчика и имя публичного проекта можно показать браузеру. Ключ оплаты, токен бота, пароль базы и ключ провайдера ИИ нужны серверу. Если код исполняется в браузере, пользователь может изучить отправленные скрипты. По официальной справке Next.js, проверенной 25 октября 2026 года, переменные с префиксом NEXT_PUBLIC_ встраиваются в браузерную сборку. Это подходящее место только для данных, которые разрешено публиковать.

Запишите для каждого значения владельца и потребителя. Например, серверный обработчик принимает сообщение и обращается к API по ключу; браузер получает только готовый ответ. Если фронтенду понадобился ключ провайдера, это сигнал изменить маршрут запроса. Для Vite публичные переменные с VITE_ тоже нельзя использовать для секретов. В моих проектах фронтенд и бэкенд часто разделены: у Radomir CRM интерфейс на React/Vite, а сервер на FastAPI. Этот стек показывает, почему нужно проверить обе стороны передачи, прежде чем добавлять переменную в сборку.

Перед тем как назвать переменную, проверьте её путь:

  • найдите процесс, которому нужен ключ;
  • отделите ответ пользователю от запроса к провайдеру;
  • проверьте, что переменная не входит в браузерную сборку;
  • запишите, где будет храниться настоящее значение.
ЗначениеГде используетсяКак хранить
Название публичного сайтаИнтерфейсВ открытой настройке
Ключ провайдера ИИСерверный запросВ закрытом окружении
Пароль базыСервер приложенияВ закрытом окружении
Идентификатор аналитикиБраузерВ публичной настройке

Что положить в проект вместо настоящего ключа

Для команды достаточно файла .env.example с именами переменных и заведомо фиктивными значениями. Он объясняет, какие настройки нужны для запуска, но не содержит действующих доступов. Настоящий ключ держите только в .env: локально и в закрытой папке сервера. Не дублируйте его в crontab, *.sh или .env.bak. Задания по расписанию и службы читают тот же закрытый файл, а копий с другим расширением не создают.

Пример ниже условный. Значение ваш_ключ не работает и не является настоящим токеном. Передавать такой файл в коммит можно только после проверки, что в нём нет реальных данных.

AI_PROVIDER_KEY=ваш_ключ
DATABASE_URL=адрес_тестовой_базы

Не пишите в коде запасной пароль на случай отсутствия переменной. Если обязательного секрета нет, приложение должно остановиться с понятной ошибкой при запуске. Иначе тестовая строка вроде dev‑pass может неожиданно стать рабочим способом входа на сервере. Сам текст ошибки не должен выводить секрет. Достаточно имени отсутствующей переменной и инструкции, где её задать.

Как проверить Git до коммита

Правило .gitignore влияет на новые неотслеживаемые файлы. Оно не убирает секрет, который уже попал в историю. Документация Git прямо отделяет эти ситуации. Проверьте и правило игнорирования, и список файлов, уже известных Git. Команды ниже не печатают содержимое ключа, но могут показать имена файлов; не копируйте в публичный чат вывод со своими внутренними путями.

git check-ignore -v --no‑index .env
git ls‑files '.env' '.env.*'
git log --all -- .env
git status --short
gitleaks git .
gitleaks dir .

Первая команда должна показать правило, которое закрывает настоящий файл. Вторая выявит уже отслеживаемые файлы; допустимый .env.example проверяйте отдельно по содержимому. git log покажет попадание .env в историю. gitleaks git . проверит историю, gitleaks dir . проверит текущие файлы до коммита. Эти команды соответствуют документации gitleaks, проверенной 26 октября 2026 года. Если настоящий ключ уже был закоммичен, одного удаления файла недостаточно: перевыпустите ключ у провайдера, оцените доступ к истории и разберите дальнейшие действия по регламенту проекта.

Кто может читать файл на сервере

Сервису нужен доступ к переменной, посторонним пользователям сервера не нужен. В моих правилах безопасности приложение запускается от отдельного пользователя, .env получает права 600, а служба не работает от root. Это проверка доступа на уровне файла и процесса. Она не заменяет ограничения прав самого ключа у провайдера: для одного проекта выдавайте отдельный ключ с минимальными возможностями и лимитом расходов.

chmod 600 .env
ls -l .env
systemctl show malyshev -p User

Команда chmod применима к вашему настоящему файлу на системе с POSIX-правами. В systemctl show замените malyshev именем своей службы и проверьте отдельного пользователя. Он должен иметь доступ к .env; другие пользователи сервера его не читают. Для службы укажите User=, EnvironmentFile= с закрытым путём к .env и NoNewPrivileges=true. Пример фрагмента unit-файла:

[Service]
User=имя_пользователя_службы
EnvironmentFile=/закрытая/папка/проекта/.env
NoNewPrivileges=true

Не переносите .env вместе с публичной папкой сайта. После таких запросов из истории выше проверьте HTTP-ответ сами. curl -I https://сайт/.env должен вернуть 404. Любой другой ответ разберите до выкладки.

Какие границы дать ИИ‑агенту

Запретите агенту читать .env и другие секретные файлы в настройках его доступа. Например, для разрешений Claude Code добавьте "deny": ["Read(./.env)"]. Для задачи «добавить отправку письма» ему достаточно имени переменной, формата ответа API и фиктивного примера. Настоящее значение подставляет человек через закрытый .env. Если агент просит секрет ради генерации кода, дайте ему фиктивное значение и точный формат ответа API.

При проверке diff смотрите не только новые строки с ключом. Секрет может попасть в текст ошибки, журнал, снимок экрана, команду запуска или клиентскую переменную. Проверяйте, что сервер возвращает пользователю только результат действия. Исходный ответ провайдера может содержать служебные сведения. Для Python-бота это особенно важно: диагностические сообщения часто уходят в чат администратора и могут включать данные запроса.

ЭтапВопросПроверка
РазработкаГде лежит настоящее значение?Только в закрытом .env
СборкаПопало ли оно в браузер?Нет секретных NEXT_PUBLIC_ и VITE_
СерверКто читает файл?Права файла и пользователь службы
СбойЧто попадёт в журнал?Только безопасная диагностика

Как проверить рабочий путь после настройки

На локальной копии запустите сервис без обязательной переменной: он должен отказать при старте. Затем задайте тестовое значение и проверьте, что код доходит до ожидаемого вызова. На сервере выполните отдельную проверку службы и одного безопасного сценария, не выводя ключ в терминал. Если ключ меняется, старое значение отключайте после подтверждения работы нового, а запись о ротации храните вне кода.

В Deyvo вместе живут веб‑приложение, FastAPI и Telegram-бот. Это пример проекта, где один секрет нужен конкретному процессу. Всем компонентам подряд давать его незачем. Раздавайте настройки адресно: токен бота нужен боту, ключ базы только тем процессам, которые с ней работают. Так утечка одного окружения затронет меньше системы.

Частые вопросы

Можно ли коммитить .env.example?

Да, если он содержит только имена переменных и фиктивные значения. Проверяйте содержимое перед каждым изменением примера.

Достаточно ли добавить .env в .gitignore?

Для нового файла это помогает. Если секрет уже отслеживался Git, правило его из истории не удалит; действующий ключ нужно заменить.

Можно ли использовать NEXT_PUBLIC_ для ключа API?

Нет, если ключ секретный. Next.js включает такую переменную в браузерную сборку. Вызов к провайдеру должен идти через сервер.

Почему служба не видит переменную после перезапуска?

Проверьте путь к файлу, пользователя службы и способ передачи окружения в её настройках. Не печатайте значение при диагностике.

Где хранить ключи нескольких проектов?

По отдельности, с минимальными правами на каждый проект и понятным владельцем. Один общий ключ усложняет замену и учёт расходов.

На обучении разбираю эту проверку на живом пути: агент меняет код, человек подставляет секрет, а перед выкладкой оба проверяют, что значение не ушло в Git и браузер.