ReactViteNginx

Деплой SPA: исправление конфликтов сборки и бесконечных циклов редиректа

Сборка падала с ERESOLVE, сервер уходил в бесконечный цикл редиректов на HTTPS, а аналитика конфликтовала с полями формы. Откуда берётся каждая проблема при деплое SPA на React и Vite и как я их закрыл.

Результат+14% конверсии формы
Александр Бутаков
Александр Бутаков
обновлено 3 мин чтения

Бесконечный цикл редиректов при деплое SPA почти всегда возникает на стыке двух слоёв: прокси или балансировщик принимает HTTPS, а до сервера приложения запрос доходит уже по HTTP — и тот снова отправляет его на HTTPS. Плюс у SPA своя особенность: сервер должен отдавать index.html на любой маршрут, которого нет на диске. В этом кейсе я переписал конфиг сервера под корректную отдачу статики и SPA-маршрутов, обошёл конфликт зависимостей при сборке и развёл аналитику с полями формы.

Что случилось

Три проблемы на пути к стабильному релизу корпоративного приложения на React и Vite:

  • сборка падала с ошибкой ERESOLVE из-за конфликта версий бандлера и библиотеки стилей;
  • при деплое сервер уходил в бесконечный цикл редиректов: маршрутизатор облачного провайдера принимал HTTPS-запрос, передавал его по внутренней сети в конфиг, который снова редиректил на HTTPS;
  • система аналитики предупреждала о перехвате функций ввода из-за контролируемых UI-компонентов.

Откуда берётся ERR_TOO_MANY_REDIRECTS

Типичная схема хостинга сегодня: снаружи стоит прокси или балансировщик, он держит SSL-сертификат и принимает HTTPS. Дальше, во внутренней сети, запрос идёт к серверу приложения обычным HTTP. Если на сервере приложения есть правило «если не HTTPS — редирект на HTTPS», он видит HTTP и редиректит. Браузер снова приходит на прокси по HTTPS, прокси снова передаёт HTTP — и так по кругу, пока браузер не сдастся с ERR_TOO_MANY_REDIRECTS.

Как это проверить: посмотрите цепочку редиректов.

Bash
# TODO: подставьте свой домен
curl -sIL https://example.com/ | grep -iE "^(HTTP|location)"

Если видите одни и те же 301 или 302 на тот же адрес много раз подряд — это оно.

Общее правило: редирект на HTTPS должен делать тот слой, который видит исходный протокол. Если это прокси — убирайте редирект с сервера приложения. Если редирект нужен именно на сервере, проверяйте не сам протокол соединения, а заголовок, который передаёт прокси, обычно X-Forwarded-Proto.

Шаг 1. Конфиг сервера для SPA

Я переписал конфиг сервера, чтобы статика и маршруты приложения отдавались корректно:

apache
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>

Что делает каждая строка: запрос к самому index.html отдаётся как есть; если запрошенного файла или каталога на диске нет — отдаётся index.html, а маршрут разбирает уже роутер приложения. Без этого правила прямой заход на /about или обновление страницы внутри приложения дают 404.

Ещё одна деталь из этого деплоя: после правок пришлось очистить кеш локальной сети, чтобы ушли фантомные ошибки сертификата — браузер и DNS какое-то время помнили старое состояние.

Шаг 2. Конфликт зависимостей при сборке

ERESOLVE — это npm сообщает, что две библиотеки требуют несовместимые версии общей зависимости. Здесь конфликтовали бандлер и библиотека стилей. Я поставил зависимости в обход строгой проверки версий:

Bash
npm install -D tailwindcss @tailwindcss/vite --legacy-peer-deps

--legacy-peer-deps — рабочий обходной путь, но не бесплатный: npm перестаёт проверять совместимость peer-зависимостей, и несовместимость может всплыть уже в рантайме. После такой установки обязательно прогоните сборку и пройдитесь по приложению руками. А когда выйдут версии, которые дружат между собой, — уберите флаг.

Шаг 3. Аналитика и поля формы

Система аналитики предупреждала о перехвате функций ввода: её запись действий пользователя конфликтовала с контролируемыми полями React. Я добавил в разметку формы блокирующий атрибут, чтобы аналитика не трогала ввод:

HTML
<form data-ym-disable-keys="true">

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

Результат

  • Оценка PageSpeed достигла 98 баллов
  • Время до первого взаимодействия 1.2 секунды
  • Конверсия захвата формы +14%
  • Ошибки редиректов полностью устранены

Чек-лист деплоя SPA

  1. Сервер отдаёт index.html на все маршруты, которых нет на диске, а статику — напрямую.
  2. Редирект на HTTPS делает ровно один слой — тот, что видит исходный протокол.
  3. Цепочка редиректов проверена через curl -sIL: не больше одного перехода.
  4. Сборка проходит без --force, или вы понимаете, почему флаг нужен, и проверили приложение руками.
  5. Скрипты аналитики не ломают контролируемые поля форм и не пишут их содержимое.

FAQ

Почему при деплое SPA возникает бесконечный редирект? Обычно прокси принимает HTTPS и передаёт запрос серверу по HTTP, а сервер снова редиректит на HTTPS. Редирект должен делать один слой — тот, что видит исходный протокол, либо сервер должен проверять заголовок X-Forwarded-Proto.

Почему SPA отдаёт 404 при обновлении страницы? Сервер ищет файл по пути маршрута и не находит его. Нужно правило, которое отдаёт index.html на любые пути, которых нет на диске.

Что значит ошибка ERESOLVE в npm? Две зависимости требуют несовместимые версии общей библиотеки. Флаг --legacy-peer-deps позволяет установить их, но совместимость тогда нужно проверить вручную.

Статья была полезной?

Нужна помощь с похожей задачей?

Веб-приложения →