Статический сайт
Статический сайт представляет собой тип сервиса, при котором платформа собирает репозиторий, берёт папку с результатом сборки и раздаёт её готовым веб-сервером. Своего процесса у такого сервиса нет: после сборки запускается не ваш код, а nginx с вашими файлами.
Тип выбирается при создании сервиса, в поле «Тип сервиса». От выбора зависит, что запустится после сборки, поэтому важно не промахнуться.
Когда выбирать
Есть ли в репозитории серверный код, который нужно запустить?
- Нет, сборка выдаёт готовые файлы → Статический сайт
- Да, свой сервер запускается и отвечает на запросы → Сервер с HTTP
Статический сайт подходит для лендинга, документации, SPA на Vite/React/Vue, сайтов на Astro, Hugo, Next.js в режиме статического экспорта. Данные такой сайт получает из внешних сервисов, запросами из браузера.
Собранный фронтенд сам по себе не делает сайт статическим. Если в репозитории есть свой сервер (Express, Fastify, NestJS), который отдаёт и API, и собранный фронтенд, выбирайте «Сервер с HTTP». То, что фронтенд написан на React, ничего не меняет.
Ошибка в эту сторону ломает приложение незаметно: сборка проходит, домен работает, но на все запросы, включая /api/…, приходит index.html. Ваш сервер при этом не запускается вообще.
Папка сборки
Единственная дополнительная настройка типа. Это относительный путь от корня репозитория до папки, куда сборка кладёт готовые файлы:
| Инструмент | Обычная папка |
|---|---|
| Vite | dist |
| Create React App, Astro | build или dist |
| Next.js (статический экспорт) | out |
| Hugo, Jekyll | public или _site |
По умолчанию подставляется dist. Если файлы уже лежат в репозитории готовыми и собирать нечего, укажите папку с ними (. означает корень репозитория). Учтите: сборщик всё равно должен определить проект по файлу-манифесту в корне, обычно это package.json. Иначе развёртывание завершится ошибкой «Не поняли, чем собирать проект».
Что происходит при развёртывании
- Репозиторий собирается обычным способом: сборщик определяет стек, ставит зависимости и выполняет сборку (для Node.js это скрипт
build). - Из результата берётся указанная папка сборки.
- Её содержимое копируется в образ с nginx, который и отдаёт файлы по вашему домену.
Отсюда особенности статического сервиса:
- Порт не задаётся. HTTP отдаёт nginx платформы, а не ваш код, поэтому поля порта у этого типа нет и переменная
PORTв коде не нужна. Требование «слушатьPORTна0.0.0.0» относится только к типу «Сервер с HTTP». - Команда запуска не нужна. Скрипт
startне используется, запускать нечего. - Клиентский роутинг работает. Любой путь, для которого нет файла, отдаёт
index.html, поэтому ссылка вида/aboutоткрывается напрямую и после перезагрузки страницы. - Домен и HTTPS выдаются как обычному Web-сервису, можно подключить собственный домен.
- Подключиться к базе данных проекта нельзя: подключаться нечему, процесса нет, а браузер во внутреннюю сеть проекта не ходит. Если сайту нужен свой API, добавьте в проект второй сервис типа «Сервер с HTTP», см. «Связь сервисов».
- Сборка по Dockerfile к этому типу не применяется: Dockerfile сам задаёт, что запускать в контейнере.
Переменные окружения
У статического сайта все переменные окружения используются на сборке, а не во время работы: контейнер с вашим кодом не запускается, читать их некому. Значения подставляет сборщик фронтенда, и попадают они прямо в собранные файлы.
Отсюда два правила:
- Изменение переменной требует нового развёртывания. Быстрый перезапуск ничего не даст: значения уже вшиты в файлы.
- Секретов в переменных быть не должно. Всё, что попало в сборку, скачивается браузером и доступно любому посетителю. Это не особенность платформы, а свойство фронтенда: ключи API с правами на запись, пароли и токены держите на серверной стороне.
Сборщики подставляют не любые переменные, а только со своим префиксом: VITE_ у Vite, NEXT_PUBLIC_ у Next.js, REACT_APP_ у Create React App. Переменная без нужного префикса до кода не дойдёт. Как задавать переменные, описано в статье «Переменные окружения и секреты».
Если тип выбран неверно
Тип можно изменить, пока не прошло ни одного успешного развёртывания: в настройках сервиса, раздел «Тип сервиса». Это и есть выход из ошибки при создании: домен, переменные и deploy key остаются на месте.
После первого успешного развёртывания тип фиксируется. Чтобы сменить его, создайте новый сервис.
| Что выбрали | Что было на самом деле | Как это выглядит |
|---|---|---|
| Сервер с HTTP | Чистая статика | Сборка проходит, но контейнер не стартует: запускать нечего. Видно сразу, чинится сменой типа |
| Статический сайт | Приложение со своим сервером | Сборка проходит, домен отвечает, но на любой запрос приходит index.html, а POST возвращает 405. Ваш код не выполняется |
Вторая ошибка тихая, поэтому по умолчанию предлагается «Сервер с HTTP»: промах в эту сторону заметен сразу.
Частые ошибки
| Симптом | Причина | Решение |
|---|---|---|
Домен отдаёт index.html вместо ответа API | Для приложения со своим сервером выбран тип «Статический сайт» | Смените тип на «Сервер с HTTP», а если развёртывание уже прошло, пересоздайте сервис |
| Развёртывание завершается, контейнер не поднимается | Наоборот: для чистой статики выбран «Сервер с HTTP» | Смените тип на «Статический сайт» |
| Развёртывание падает уже после успешной сборки или домен отдаёт 404 | Указана папка, которой нет в результате сборки, либо в ней нет index.html | Сверьте путь с конфигурацией сборщика (dist, build, out) |
| Развёртывание падает: «Не поняли, чем собирать проект» | В корне репозитория нет файла-манифеста | Добавьте package.json (или манифест своего стека) и проверьте, что он закоммичен |
| Изменил переменную, на сайте старое значение | Значения вшиваются на сборке | Разверните сервис заново |
| Переменная не видна в коде | Нет префикса сборщика | Переименуйте: VITE_…, NEXT_PUBLIC_…, REACT_APP_… |
| Сайт не может подключиться к базе данных проекта | У статики нет серверного процесса, браузер во внутреннюю сеть не ходит | Добавьте отдельный сервис с API, см. «Связь сервисов» |