Манифест 12-факторных приложений внес весомый вклад в процесс разработки и эксплуатации веб-приложений, но это по-большей части коснулось бекендов, и обошло стороной фронтенды. Большенство пунктов манифеста или не применимы к фронтендам, или выполняются сами собой, но с номером 3 — конфигурация — есть вопросики. В оригинальном манифесте сказано: «Сохраняйте конфигурацию в среде выполнения». На практике это означает, что конфигурацию нельзя хранить внутри исходного кода или финального артефакта. Ее нужно передавать в приложение по время запуска. У этого правила есть практические применения, вот парочка: Приложение в разных окружениях должно обращаться к разным бекендам. На продакшене — к продакшн API, на стейдженге — к стейджинг API, а при запуске интеграционных тестов — к специальному мок-серверу. Для е2е тестов нужно снижать время ожидания реакции пользователя.  Например, на если на сайте после 10 минут бездействия что-то происходит, то для тестового сценария можно уменьшить этот интервал до минуты. Если фронтенд-приложение содержит в себе SSR, то задача становится чуточку легче. Конфигурацию передают как переменные окружения в приложение на сервере, при рендере она попадает в ответ клиенту как глобальные переменные, объявленные в <script> в самом начале страницы. На клиенте же достаточно эти переменные подхватывать из глобальной области видимости и использовать. Недавно мы в Авиасейлс делали приложение для серверного рендеринга кусочков сайта и столкнулись с этой задачей. Результат мой тиммейт заопенсорсил  — isomorphic-env-webpack-plugin. Прекрасный Next.js умеет так из коробки, ничего специально делать не нужно. Другое дело, если приложение — набор статических файлов. Переменные окружения нельзя передать на клиент, ведь мы не контролируем сервер, а все файлы уже заранее сгенерированы и лежат на диске. В таком случае, на этот фактор часто забивают. Два самых популярных способа хранить конфигурацию фронтенд приложения с клиентским рендерингом: Положить к исходным текстам – завести в коде файлик config.js, и сложить в него параметры. Это работает, но некоторые крайние случаи могут сильно испортить жизнь. Часто в таких файлах появляется проверка — если текущий хост такой-то, то ходить на дев-бекенд, если хост другой — ходить на прод-бекенд. Такой подход плохо дружит с переменным количеством окружений — когда на каждый PR поднимается новый стенд. Передавать при сборке артефакта. Обычно это делают через DefinePlugin для Webpack. Этот подход лучше первого — для каждого окружения можно собирать отдельный артефакт и добавлять туда специфичные параметры. Возникает проблема — на продашкн поедет не тот же артефакт, что тестировался. Технически, нет гарантии, что в разных окружениях версия будет работать одинаково. Иногда это приводит к печальным последствиям. Есть несколько способов получше. Подавляющее большенство фронтенд-приложений подчиняются двум критериям: Файлы ресурсов раздаются через nginx, либо перед приложением стоит nginx как реверс-прокси. Тут nginx можно заменить на любой аналог. Единственная конфигурация, необходимая приложению — адреса разных API. Для таких приложений проблему конфигурации можно решить так — в клиентском коде все запросы отправить на текущий домен, а на стороне nginx роутить эти запросы в конкретные бекенды. Будем отправлять запросы на /user-api/path вместо https://user.my-service.io/path, на /auth-api/path вместо https://auth.other-service.io/path и так далее. Дальше инструкция специфична для nginx в Docker-контейнере Начиная с версии 1.19 официальный Docker-образ nginx умеет использовать переменные окружения в конфигурационных файлах. Для этого нужно создать файл конфигурации с суффиксом .template и поместить его в директорию /etc/nginx/templates. При старте сервер подхватит переменные окружения, пройдёт по шаблонам и создаст финальные файлы конфигурации. Типичная конфигурация nginx для SPA будет выглядеть так: Dockerfile в этом случае будет примерно таким: Теперь достаточно запустить контейнер и передать переменные окружения, на основе которых nginx создаст конфигурационные файлы.  Так, косвенным образом, фронтенд приложение получит параметры в момент запуска. Живой пример можно посмотреть в этом проекте. Такую схему можно реализовать и с другими серверами. Caddy поддерживает переменные окружения в конфигурационных файлах из коробки, а Traefik умеет в динамические конфигурации. Если в приложении конфигурация не исчерпывается путями до API, вариант с прокси-сервером для хранения параметров не подходит. Проделав этот путь можно пойти дальше и генерировать конфигурационные файлы не для прокси-сервера, а напрямую для фронтенда. Это позволит передавать любые параметры, не зависеть от способа раздачи статических файлов и места их хранения.  При старте приложения можно подхватывать переменные окружения и записывать их в JS-файл: А потом раздавать этот файл тем же способом, что и остальные статические файлы. На клиенте забирать параметры из этой глобальной переменной и использовать в приложении. Дополнительно нужно будет добавить <script> в HTML-страницу с приложением. Дальше инструкция специфична для nginx в Docker-контейнере Важно отметить, что отправлять все переменные окружения на клиент может быть опасно, часто в них хранится приватная информация — ключи доступа до API, пароли и токены. Поэтому, лучше явно перечислить имена переменных, которые будут отправлены в браузер в файле env.dict: Теперь простым Bash-скриптом generate_env.sh будем доставать значения из окружения и складывать в JS-файл: Я не большой знаток Bash, вероятно получилось странно. Этот скрипт призван показать общую идею, а не использоваться в проекте напрямую. Теперь при старте контейнера вместо запуска nginx нужно выполнить этот скрипт, а потом уже запустить nginx. Заведём точку входа cmd.sh, которая сделает это:  И теперь немного подправим Dockerfile: После этих манипуляций можно передавать на фронтенд любые параметры через переменные окружения — нужно отметить переменную в env.dict и передать ее при запуске контейнера. Живой пример можно посмотреть в этом проекте. Если внимательно посмотреть на этот вариант, станет понятно, что он почти не отличается от варианта с SSR приведённого в начале статьи. Для удобства можно воспользоваться isomorphic-env-webpack-plugin, и дописать пару скриптов: генерации файла и вставки ссылки на него в HTML. В этой схеме есть еще один небольшой эдж-кейс — обычно в имена файлов с ресурсами добавляют хеш содержимого, чтобы в браузере без проблем кешировать все файлы навсегда по имени. В таком случае нужно немного усложнить скрипт генерации файла с переменными, хешировать содержимое и добавлять результат в имя файла.  Правильная работа с параметрами фронтенд-приложения помогает создавать надежные и удобные в эксплуатации системы. Достаточно отделить конфигурацию от приложения и перенести ее в среду выполнения, чтобы радикально улучшить комфорт членов команды и снизить количество потенциальных ошибок. А как вы передаёте конфиги в клиентские приложения? Software Engineer