Магия Nginx и React Router: Как заставить SPA работать как часы

Магия Nginx и React Router: Как заставить SPA работать как часы

Вы создали красивое React-приложение с клиентской маршрутизацией, но при обновлении страницы или прямом переходе по URL получаете предательскую ошибку 404? Это классическая проблема SPA, которую решает правильная конфигурация веб-сервера. В этой статье мы разберем, как настроить Nginx для безупречной работы с React Router, чтобы ваше приложение вело себя как полноценное веб-приложение, а не как набор статических файлов.

Почему React Router и Nginx конфликтуют?

React Router — это клиентский маршрутизатор. Он управляет переходами между «страницами» вашего приложения, не запрашивая новый HTML с сервера. Когда пользователь заходит на /about прямо из адресной строки, Nginx ищет папку или файл about в корневой директории, не находит его и возвращает 404. Серверу нужно «объяснить», что все запросы (кроме статических файлов) должны перенаправляться на index.html.

Важно: Эта настройка актуальна для React Router v4+, Vue Router, и других SPA-фреймворков с HTML5 History API (не HashRouter).

Базовая конфигурация Nginx

Создайте или отредактируйте конфигурационный файл для вашего сайта (обычно в /etc/nginx/sites-available/). Вот минимальная рабочая конфигурация:

server {
    listen 80;
    server_name yourdomain.com;
    root /var/www/your-react-app/build;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control \"public, immutable\";
    }
}

Разбираем ключевую директиву

Волшебство происходит в блоке location /:

  • try_files $uri $uri/ /index.html — директива пытается найти запрошенный файл ($uri) или папку ($uri/). Если ничего не найдено, сервер отдает index.html.
  • React Router, загрузившись из index.html, «увидит» URL /about в адресной строке и отрендерит соответствующий компонент.

Продвинутая настройка для продакшена

Для реального проекта нужно добавить сжатие, безопасные заголовки и обработку ошибок:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    root /var/www/your-app/build;
    index index.html;

    # Gzip сжатие
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;

    location / {
        try_files $uri $uri/ /index.html;
        # Безопасные заголовки
        add_header X-Frame-Options \"SAMEORIGIN\";
        add_header X-Content-Type-Options \"nosniff\";
    }

    # Статические файлы с долгим кэшированием
    location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control \"public, immutable\";
        access_log off;
    }

    # Кастомная страница 404 (опционально)
    error_page 404 /index.html;
}

Совет: Всегда проверяйте конфигурацию командой sudo nginx -t перед перезагрузкой сервера (sudo systemctl reload nginx).

Особые случаи: API-прокси и поддиректории

Проксирование API-запросов

Если ваш фронтенд общается с бэкендом на другом порту/домене:

location /api/ {
    proxy_pass http://localhost:3000; # Ваш бэкенд
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

Размещение приложения в поддиректории

Если React-приложение должно работать по пути /app:

  1. Настройте basename в React Router: <BrowserRouter basename=\"/app\">
  2. В Nginx укажите: root /var/www/your-app/build; и try_files $uri $uri/ /app/index.html;

FAQ: Часто задаваемые вопросы

Почему после настройки стили/скрипты не грузятся?

Проверьте базовый путь (publicPath) в сборке. В Create React App можно задать \"homepage\" в package.json или использовать переменную окружения PUBLIC_URL.

Как настроить для HashRouter (#)?

Для HashRouter настройка Nginx не требуется — всё после # не отправляется на сервер. Но этот режим устарел и имеет недостатки для SEO.

Нужно ли настраивать что-то для Docker-развертывания?

Конфигурация идентична. Просто скопируйте nginx.conf в контейнер и укажите его как точку входа. Используйте официальный образ Nginx на Alpine для минимального размера.

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

1. Разместите build-папку в корневой директории Nginx. 2. Перейдите на корневой URL — должно открыться приложение. 3. Вручную введите путь, например, /dashboard — не должно быть 404. 4. Обновите страницу на любом маршруте — приложение должно остаться.