🔀
Хостинг

Nginx reverse proxy: как вывести Node.js и Python-приложение на домен

13.11.2026
← Все статьи

Приложение на Node.js, Python (Django, FastAPI, Flask) или другой современной платформе обычно запускается со своим встроенным сервером и слушает порт вроде 3000, 5000 или 8000. Если открыть в браузере http://ip-сервера:3000, всё работает, но давать такой адрес клиентам нельзя. Пользователь вводит example.uz и обращается на порт 80 или 443. Этот разрыв закрывает Nginx в роли reverse proxy: он принимает внешний запрос, передаёт его приложению и возвращает ответ клиенту.

Что на самом деле делает обратный прокси

Обычный прокси стоит на стороне пользователя и выходит в интернет от его имени. Обратный прокси стоит на стороне сервера, закрывает внутренние сервисы от внешнего мира и отвечает за них. Браузер клиента общается только с Nginx и не знает, на каком порту и в скольких экземплярах работает приложение. Это повышает безопасность: порт приложения не открыт наружу и слушает только 127.0.0.1.

Кроме того, Nginx берёт на себя SSL-шифрование, сам раздаёт статические файлы (изображения, CSS, JavaScript), не нагружая приложение, включает сжатие и может распределять нагрузку между несколькими экземплярами. Node.js или Gunicorn способны делать многое из этого самостоятельно, но Nginx создан именно для таких задач и держит тысячи соединений при очень скромном расходе памяти.

Пример: API службы доставки в Ташкенте

Допустим, небольшая служба доставки в Ташкенте написала API для приёма заказов на Node.js, и оно работает на VPS на порту 3000. Админ-панель написана на Python FastAPI и работает на порту 8000. Задача: api.example.uz должен вести на Node.js, admin.example.uz на FastAPI, и оба адреса должны открываться по HTTPS. Сначала в DNS для обоих поддоменов создаются A-записи на IP сервера, затем настраивается Nginx.

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

На Ubuntu и Debian конфигурации сайтов обычно лежат в /etc/nginx/sites-available/. Следующий блок передаёт запросы для api.example.uz приложению на Node.js:

server {
    listen 80;
    server_name api.example.uz;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Чтобы включить конфигурацию и перечитать её, используются команды ниже. nginx -t проверяет синтаксис, и запускать её перед каждым reload стоит сделать привычкой:

sudo ln -s /etc/nginx/sites-available/api.example.uz /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Зачем нужны заголовки

Когда Nginx передаёт запрос дальше, для приложения все запросы выглядят так, будто пришли с 127.0.0.1. Без заголовков X-Real-IP и X-Forwarded-For реальный IP клиента теряется: в логах его нет, ограничения по IP и статистика работают неверно. X-Forwarded-Proto сообщает приложению, что запрос пришёл по HTTPS. Без него Django или Express могут решить, что работают по HTTP, и уйти в бесконечный цикл редиректов.

На стороне приложения доверие к этим заголовкам тоже нужно включить. В Express это app.set('trust proxy', 1), в Django настройка SECURE_PROXY_SSL_HEADER, в Uvicorn параметр --proxy-headers.

WebSocket и приложения реального времени

Если используется WebSocket для чата, отслеживания статуса заказа или уведомлений, базовой настройки недостаточно. WebSocket открывается через механизм Upgrade HTTP-соединения, а Nginx сам эти заголовки не передаёт. В блок location нужно добавить:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;

Значение proxy_read_timeout важно: по умолчанию Nginx закрывает соединение, если 60 секунд от бэкенда ничего не приходит. Для долгоживущих WebSocket это значение увеличивают или настраивают периодический ping со стороны приложения.

Python-приложения: Gunicorn и Uvicorn

Django обычно запускают через Gunicorn, FastAPI через Uvicorn. В обоих случаях приложение стоит привязать только к локальному адресу, иначе порт останется открытым снаружи:

gunicorn myproject.wsgi:application --bind 127.0.0.1:8000 --workers 3
uvicorn main:app --host 127.0.0.1 --port 8000 --proxy-headers

Статику Django гораздо быстрее отдавать напрямую через Nginx, а не через Gunicorn. Для этого папка, собранная командой collectstatic, указывается в отдельном блоке location /static/, и приложение занимается только динамическими запросами.

Подключение SSL-сертификата

Когда сайт заработал по HTTP, сертификат Let's Encrypt можно получить через Certbot. Он сам отредактирует конфигурацию Nginx, добавит блок для порта 443 и включит перенаправление с HTTP на HTTPS:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d api.example.uz -d admin.example.uz

Сертификат действует 90 дней, а при установке Certbot добавляется таймер автоматического продления. Проверить, что продление сработает, можно командой sudo certbot renew --dry-run.

Чтобы приложение работало постоянно

Обратный прокси работает только тогда, когда жив бэкенд. Если процесс Node.js завершается при закрытии терминала, Nginx вернёт ошибку 502 Bad Gateway. Поэтому приложение запускают как службу systemd или через менеджер процессов вроде PM2. Параметр Restart=always в юните systemd перезапустит приложение при падении, а systemctl enable обеспечит автозапуск после перезагрузки сервера.

Самые частые ошибки

Где это можно сделать

Для полного управления конфигурацией Nginx нужен root-доступ, то есть VPS или выделенный сервер. VPS от sayt.uz размещены в дата-центре в Узбекистане, поэтому задержка для местных пользователей минимальна. Для обычного PHP-сайта всё это не требуется: на виртуальном хостинге Nginx и PHP уже настроены. Узнать, можно ли запустить Node.js или Python-приложение на shared-хостинге, можно в кабинете sayt.uz или через техническую поддержку.

Похожие статьи

📤 Не загружается большой файл: upload_max_filesize и post_max_size ⚡ Как измерить скорость сайта в Tas-IX: команды и разбор результатов 🔌 WebSocket на хостинге: обычный хостинг, VPS и настройка Nginx 🐢 Лог медленных запросов MySQL: как найти проблемный запрос
🌐 Язык
🇺🇿 O'zbek 🇺🇿 Ўзбек 🇷🇺 Русский ✓ 🇬🇧 English