Проксирование в nginx — это когда веб-сервер принимает запрос из интернета на порт 80 или 443, а затем передаёт его дальше, вашему приложению, которое слушает локальный порт вроде 3000 или 8000. Само приложение при этом закрыто от внешнего мира, а nginx берёт на себя HTTPS, сжатие ответов, отдачу картинок и распределение нагрузки между несколькими копиями кода. Настраивается схема одной директивой proxy_pass и четырьмя строками заголовков, а работает даже на самом дешёвом VPS с nginx: хватает 1 ядра и 1 ГБ памяти.
Материал для тех, кто уже написал сайт на Node.js, Python или PHP-фреймворке и запустил его на сервере. Осталось непонятным одно: как открыть его миру по нормальному домену вместо адреса с портом. Разберём минимальный конфиг, обязательные заголовки, сертификат, WebSocket, балансировку и ошибки 502 и 504.
Что такое обратный прокси и что он даёт приложению
Обратный прокси — это сервер-посредник, который стоит перед вашим приложением и принимает на себя все входящие запросы. Клиент из браузера видит только его, о существовании приложения на localhost:3000 он не знает вообще. Обратный он потому, что защищает и обслуживает сторону сервера, а не сторону клиента. В связке участвуют три стороны: браузер, nginx на портах 80 и 443 и приложение на локальном порту 3000.
Встроенные веб-серверы фреймворков — Gunicorn у Python, http-модуль у Node.js — плохо переносят медленных клиентов и потоки статики. Nginx спроектирован ровно под это: один рабочий процесс держит тысячи одновременных соединений при потреблении в 10–20 МБ памяти.
Второй аргумент — безопасность. Приложение слушает 127.0.0.1, то есть внутренний интерфейс сервера, и снаружи к нему подключиться нельзя: открытым остаётся один порт 443. Третий — гибкость: под одним доменом собирают несколько приложений, отдавая путь /api на порт 8000, а остальное на 3000. И наконец, прокси — единственная точка, где удобно держать сертификат и кеш.
Минимальный конфиг: одна директива proxy_pass
Конфигурация живёт в каталоге /etc/nginx. Виртуальные хосты (в терминологии nginx — блоки server) кладут отдельными файлами в /etc/nginx/sites-available и включают символической ссылкой из /etc/nginx/sites-enabled. Если веб-сервер ещё не стоит, начните с установки nginx на Ubuntu.
Самый короткий рабочий вариант выглядит так:
server {
listen 80;
server_name example.ru www.example.ru;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
Директива listen 80 велит принимать обычные HTTP-запросы, server_name задаёт домены блока, а proxy_pass пересылает запрос приложению. Адрес пишем как 127.0.0.1, а не localhost: во втором случае nginx может пойти по IPv6-адресу ::1, где приложение не слушает, и вернуть 502.
Обратите внимание на слеш в конце адреса в proxy_pass. Без слеша путь уходит как есть: /users придёт бэкенду как /users. А запись со слешем внутри блока location /api/ отрежет префикс /api — приложение получит просто /. Это самая частая причина внезапных ошибок 404 после переезда. Если сомневаетесь, начинайте без слеша и смотрите в логи приложения, какой путь до него доходит.

Заголовки: без них приложение видит вместо клиента сам nginx
Конфиг выше уже работает, но приложение в такой схеме слепо: оно видит запрос с адреса 127.0.0.1, домен 127.0.0.1:3000 и протокол http. В логах будет один IP на все посещения, аналитика сломается, а редиректы уведут пользователя на несуществующий адрес.
Чинится это директивами proxy_set_header: каждая добавляет к запросу служебный заголовок с настоящими данными клиента. Переменные вроде $host и $remote_addr nginx подставляет сам.
| Строка конфига | Что передаёт бэкенду | Что сломается без неё |
|---|---|---|
| proxy_set_header Host $host; | Домен из адресной строки | Редиректы и cookie уводят на 127.0.0.1:3000 |
| proxy_set_header X-Real-IP $remote_addr; | IP-адрес посетителя | В логах приложения один адрес на всех |
| proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | Цепочку адресов, если прокси несколько | Не работают лимиты и баны по IP |
| proxy_set_header X-Forwarded-Proto $scheme; | Протокол: http или https | Приложение строит ссылки на http, браузер ругается на смешанный контент |
Эти четыре строки добавляют внутрь блока location, сразу после proxy_pass. Чтобы не копировать их в каждый виртуальный хост, вынесите их в файл /etc/nginx/proxy_params и подключайте строкой include proxy_params;. Само приложение тоже надо научить доверять заголовкам: в Django это USE_X_FORWARDED_HOST, в Express — app.set('trust proxy', 1).
Пошагово: настраиваем проксирование на Ubuntu за 20 минут
Ниже маршрут от чистого сервера до работающего домена. Предполагаем Ubuntu 24.04, приложение уже запущено как служба systemd и слушает порт 3000. Systemd — штатный менеджер служб в Linux: он сам поднимает программу после перезагрузки сервера. Команды выполняйте от root (пользователя с полными правами) или через sudo.
Проверяйте каждый шаг отдельно, а не пишите весь конфиг сразу. Если сервера ещё нет, подойдёт любой тариф из подборки VPS с Ubuntu 24.04: 1 ядра и 1 ГБ памяти хватит для сайта с посещаемостью до нескольких тысяч человек в сутки.
- Проверьте, что приложение слушает локальный порт:
ss -tlnp | grep 3000. Команда показывает, какие программы заняли какие порты. В выводе должен быть адрес 127.0.0.1:3000, а не 0.0.0.0:3000. - Установите веб-сервер:
apt update && apt install nginx -y. Проверьте, что он поднялся:systemctl status nginx. - Откройте в файрволе (сетевом фильтре сервера) только веб-порты:
ufw allow 80,443/tcp. Порт 3000 наружу открывать не нужно. - Создайте файл /etc/nginx/sites-available/app.conf с блоком
serverиз примеров выше — сproxy_passи четырьмя заголовками. - Включите сайт и уберите дефолтный:
ln -s /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/иrm /etc/nginx/sites-enabled/default. - Проверьте синтаксис командой
nginx -t— она покажет строку с ошибкой. Только после успешной проверки применяйте:systemctl reload nginx. - Направьте A-запись домена на IP сервера. A-запись — строка в настройках домена, которая говорит браузеру, на какой адрес идти. Подождите 5–30 минут и откройте сайт в браузере.
Команда reload отличается от restart тем, что не рвёт активные соединения: старые процессы дорабатывают запросы, новые стартуют с обновлённым конфигом.
HTTPS на фронте: сертификат живёт в nginx, а не в приложении
Когда nginx стоит перед приложением, шифрование завершают на нём. Схема называется SSL-termination: снаружи идёт защищённое соединение по SSL/TLS, а внутри сервера трафик бежит по обычному http на 127.0.0.1 и за пределы машины не выходит. Приложению про сертификаты знать не нужно.
Бесплатный сертификат Let’s Encrypt выдаётся на 90 дней и продлевается автоматически. Ставят его утилитой certbot: её плагин для nginx сам допишет в конфиг пути к сертификату и редирект с 80 порта на 443. Пошаговый разбор — в руководстве про установку SSL от Let’s Encrypt.
После установки блок сервера будет содержать listen 443 ssl;, строки ssl_certificate и ssl_certificate_key, а также отдельный блок с listen 80; и постоянным редиректом. Не забудьте добавить proxy_set_header X-Forwarded-Proto $scheme; — без него приложение продолжит считать, что работает по http. Заодно включите HTTP/2 директивой http2 on;: на странице с 40–60 мелкими файлами он экономит заметную долю времени загрузки.
WebSocket и долгие соединения через прокси
Чаты, уведомления и панели мониторинга держат постоянное соединение по протоколу WebSocket. Оно начинается как обычный HTTP-запрос, но потом клиент просит сервер переключить протокол заголовком Upgrade. По умолчанию nginx его бэкенду не передаёт, и соединение обрывается с ошибкой 400 — типичная жалоба «локально чат работает, на сервере нет».
Лечится тремя строками в блоке location, который отвечает за сокеты:
location /ws/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
}
Строка proxy_http_version 1.1 обязательна: по умолчанию к бэкенду nginx ходит по HTTP/1.0, а тот переключение протокола не поддерживает. Директива proxy_read_timeout задаёт, сколько ждать данных от приложения — стандартные 60 секунд для чата мало, разумно ставить от 600 до 3600 секунд.
Для длинных потоков ответов (стриминг текста, server-sent events) дополнительно отключают буферизацию строкой proxy_buffering off;. Без этого nginx копит ответ и отдаёт его целиком, из-за чего текст появляется не по мере генерации, а разом в конце.
Балансировка: блок upstream и распределение между копиями приложения
Когда одного процесса перестаёт хватать, запускают несколько копий приложения на портах 3000, 3001, 3002 и распределяют запросы между ними. В nginx это делает блок upstream: он описывает группу серверов, а proxy_pass ссылается на неё по имени.
upstream app_backend {
least_conn;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002 backup;
keepalive 32;
}
server {
listen 80;
location / {
proxy_pass http://app_backend;
include proxy_params;
}
}
По умолчанию действует round-robin: запросы уходят серверам по очереди поровну. Остальные режимы включаются одной строкой внутри блока.
| Режим | Строка в upstream | Как распределяет | Когда выбирать |
|---|---|---|---|
| Round-robin | ничего писать не нужно | По очереди, поровну | Одинаковые копии, быстрые ответы |
| Least connections | least_conn; | Тому, у кого меньше активных соединений | Отчёты, выгрузки, долгие запросы |
| IP hash | ip_hash; | Один клиент всегда на один сервер | Сессии хранятся в памяти процесса |
| Веса | server …:3001 weight=3; | Пропорционально числу, тут 3 к 1 | Серверы разной мощности |
| Резерв | server …:3002 backup; | Только когда основные не отвечают | Страховка на время обновления |
Строка keepalive 32 держит до 32 постоянных соединений с бэкендом и снимает по 1–3 мс с каждого запроса. Если балансировать нужно между разными машинами, вместо 127.0.0.1 указывают их внутренние адреса; для сложных сценариев с проверками здоровья чаще берут HAProxy.
Ошибки 502 и 504: буферы, таймауты и размер тела запроса
Почти все проблемы проксирования сводятся к четырём кодам ответа. Логи лежат в /var/log/nginx/error.log — открывайте их командой tail -n 50 /var/log/nginx/error.log сразу после того, как воспроизвели ошибку, и читайте последнюю строку.
Ключевое различие: 502 означает, что nginx не смог достучаться до приложения вообще, а 504 — что достучался, но не дождался ответа. Первое чинят на стороне приложения, второе — таймаутами или оптимизацией кода.
| Код | Что произошло | Что делать |
|---|---|---|
| 502 Bad Gateway | Приложение упало или слушает другой порт | Проверить systemctl status и ss -tlnp |
| 504 Gateway Time-out | Ответ шёл дольше 60 секунд по умолчанию | Поднять proxy_read_timeout до 120–300 секунд |
| 413 Request Entity Too Large | Файл больше лимита в 1 МБ | Задать client_max_body_size 50m; |
| 400 Bad Request на сокетах | Не переданы Upgrade и Connection | Добавить три строки из раздела выше |
Ещё один симптом — предупреждение в логе о том, что ответ записан во временный файл: он не поместился в буфер по умолчанию (8 блоков по 4 или 8 КБ). Лечится строкой proxy_buffers 16 16k;.

Статика мимо приложения и кеширование ответов
Отдавать картинки, шрифты и скрипты через приложение расточительно: каждый такой запрос занимает процесс, который мог бы считать бизнес-логику. Nginx отдаёт файл с диска на порядок дешевле, поэтому статику перехватывают отдельным блоком location до общего правила.
location /static/ {
alias /var/www/app/static/;
expires 30d;
access_log off;
}
Директива expires 30d добавляет к ответу заголовок с датой устаревания, и браузер 30 дней берёт файл из своего кеша, не спрашивая сервер. Чтобы обновления не залипали у пользователей, к именам файлов при сборке добавляют хеш — app.4f21c8.js. Подробнее механика разобрана в справке про кеширование.
Отдельно существует кеш самих ответов приложения — proxy_cache. Он складывает готовый HTML на диск и следующие запросы обслуживает без обращения к бэкенду. Для блога или витрины магазина кеш на 60 секунд снимает с приложения до 90 % нагрузки, а для личных кабинетов и корзины его включать нельзя: один пользователь увидит страницу другого.
Если поток запросов заметный, nginx становится и первым рубежом фильтрации: limit_req ограничивает частоту обращений с одного адреса, а серьёзные атаки отсекают на стороне провайдера — такие тарифы собраны в подборке защита от DDoS для nginx.
Сколько ресурсов и денег нужно под связку nginx и приложение
Сам nginx почти ничего не потребляет: процесс с сотней активных соединений укладывается в 15–25 МБ памяти. Основной аппетит у приложения. Ориентир: визитке и небольшому API хватает 1 ядра и 1 ГБ, магазину на PHP или Django — 2 ядер и 2–4 ГБ, проекту с очередями и базой на том же сервере — 4 ядер и 8 ГБ.
Диск берите NVMe: на HDD логи и кеш ответов тормозят отдачу. В разделе каталога собраны 44 провайдера с поддержкой nginx, ниже — их стартовые тарифы.
| Провайдер | Стартовая цена | Базовая конфигурация | Тарифов | Рейтинг |
|---|---|---|---|---|
| Cloudcore | от 59 ₽/мес | 1 ядро, 1 ГБ, 10 ГБ NVMe | 12 | 3.3 |
| Hosting-Russia | от 99 ₽/мес | уточняется у провайдера | — | 3.0 |
| HostVDS | от 127 ₽/мес | 1 ядро, 1 ГБ, 10 ГБ NVMe | 12 | 3.3 |
| RuVDS | от 139 ₽/мес | 1 ядро, 0,5 ГБ, 10 ГБ HDD | 440 | 4.3 |
| Timeweb | от 180 ₽/мес | 15 ГБ NVMe | 77 | 4.3 |
| Selectel | от 200 ₽/мес | 1 ядро, 1 ГБ, 10 ГБ NVMe | 69 | 4.5 |
| AdminVPS | от 299 ₽/мес | 1 ядро, 1 ГБ, 15 ГБ NVMe | 149 | 4.5 |
Данные проверены 04.09.2026.
Тариф за 59 ₽ годится, чтобы потренироваться на учебном проекте. Для боевого сайта берите минимум 2 ГБ памяти: при 1 ГБ сборка фронтенда упирается в потолок памяти, и система убивает процесс. Переход с тарифа за 139 ₽/мес на конфигурацию с 2 ГБ обойдётся дороже примерно на 150–300 ₽/мес.
Частые ошибки при настройке проксирования
Ошибки почти всегда одни и те же, и все они видны либо в nginx -t, либо в первых строках error.log. Пробегитесь по списку ниже, прежде чем искать проблему в коде приложения. И помните: после каждой правки конфига нужна пара команд nginx -t и systemctl reload nginx — правка без перезагрузки просто не применяется.
- Забыли
proxy_set_header Host $host;— приложение шлёт редиректы на 127.0.0.1:3000, и пользователь попадает в никуда. - Указали
proxy_pass http://localhost:3000;— при включённом IPv6 запрос уходит на ::1 и возвращается ошибкой 502. - Приложение слушает 0.0.0.0 вместо 127.0.0.1 — порт 3000 доступен снаружи, и весь смысл прокси теряется.
- Перепутали слеш в конце
proxy_passвнутри location с префиксом — бэкенд получает обрезанный или задвоенный путь и отвечает 404. - Не подняли
client_max_body_size— загрузка файла тяжелее 1 МБ падает с ошибкой 413. - Оставили в sites-enabled дефолтный конфиг — он перехватывает запросы к серверу по IP и показывает приветствие вместо сайта.
Частые вопросы
Нужен ли отдельный сервер под nginx? Нет. В большинстве проектов nginx и приложение живут на одной машине и общаются через localhost — так быстрее и дешевле. Разносить их стоит, только когда за одним прокси стоит несколько независимых бэкендов.
Что выбрать — nginx или Apache? Apache тоже умеет работать обратным прокси через mod_proxy, но на статике и большом числе соединений проигрывает: поток на каждое соединение съедает память быстрее. Сравнение с цифрами — в разборе nginx или Apache.
Почему после включения HTTPS сайт ругается на смешанный контент? Приложение не знает, что снаружи шифрование, и генерирует ссылки на http. Добавьте proxy_set_header X-Forwarded-Proto $scheme; и включите доверие к этому заголовку в настройках фреймворка — после перезапуска ссылки станут защищёнными.
Сколько сайтов можно проксировать с одного сервера? Ограничение задаёт не nginx, а память под сами приложения: на тарифе с 4 ГБ живут 5–8 небольших сайтов на PHP или 3 приложения на Node.js. Каждый домен описывается своим блоком server и своим сертификатом.
Обязательно ли держать приложение как службу systemd? Практически да. Запуск из терминала умрёт вместе с закрытием сессии, а служба поднимется сама после перезагрузки сервера и после падения процесса. Без этого любая ошибка в коде превращается в 502 до тех пор, пока вы вручную не зайдёте на сервер.
Подойдёт ли обычный виртуальный хостинг вместо VPS? Нет: там нет доступа к конфигам nginx и нельзя держать свой процесс на произвольном порту. Как только приложению нужен собственный демон, требуется сервер с root-доступом — от 59 ₽/мес.
Итог
Рабочая схема собирается из четырёх элементов: приложение слушает 127.0.0.1:3000, nginx принимает запросы на 443, четыре директивы proxy_set_header передают данные клиента, а сертификат Let’s Encrypt продлевается каждые 90 дней. Балансировку добавит блок upstream, а разбор проблем всегда начинается с nginx -t и последних 50 строк error.log.
Дальше остаётся выбрать площадку: конфигурации и цены 44 провайдеров собраны в разделе VPS для nginx — от 59 ₽ за учебный сервер до тарифов с 8 ГБ памяти.