MHOSTMHOST

Как настроить Nginx как reverse proxy: пошаговая инструкция

Reverse proxy (обратный прокси) — это сервер, который принимает запросы из интернета и передаёт их приложению на внутреннем порту. Посетитель открывает https://app.example.com, а Nginx незаметно отправляет запрос на 127.0.0.1:3000, где работает ваше приложение, и возвращает ответ.

Зачем он нужен:

  • Несколько сервисов на одном сервере. Порты 80 и 443 может занимать только одна программа. Nginx принимает все запросы и раздаёт их по доменам: app.example.com на порт 3000, n8n.example.com на порт 5678.
  • HTTPS в одном месте. Сертификат настраивается в Nginx, а приложения работают по обычному HTTP и ничего не знают про SSL.
  • Приложение не торчит в интернет. Оно слушает только localhost, снаружи доступен только Nginx.
  • Дополнительная защита и удобство. Лимиты запросов, вход по паролю, сжатие, ограничение размера загрузки — всё настраивается в прокси, без правок в коде приложения.

Reverse proxy нужен почти для всего, что запускают на VPS: приложений на Node.js и Python, Telegram-ботов на вебхуках, n8n, сервисов в Docker.

Требования:

  • Сервер на Ubuntu 22.04, 24.04 или 26.04 и пользователь с sudo.
  • Приложение, которое работает на локальном порту. В примерах это порт 3000.
  • Домен или поддомен, A-запись которого указывает на IP сервера.

Шаг 1. Установите Nginx и проверьте приложение

Установите Nginx и откройте веб-порты в firewall:

sudo apt update

sudo apt install -y nginx

sudo ufw allow 'Nginx Full'

Откройте в браузере http://IP_СЕРВЕРА — должна появиться страница «Welcome to nginx!».

Теперь убедитесь, что приложение отвечает локально:

curl -I http://127.0.0.1:3000

В ответе должна быть строка вроде HTTP/1.1 200 OK. Если вместо неё Connection refused, приложение не запущено или слушает другой порт. Сначала почините его: Nginx не сможет проксировать запросы на неработающее приложение и будет отдавать ошибку 502.

Посмотреть, какие порты слушают программы на сервере: sudo ss -tlnp.

Шаг 2. Создайте конфиг reverse proxy

Создайте файл конфигурации для вашего домена. Замените app.example.com на свой домен:

sudo nano /etc/nginx/sites-available/app.example.com

Вставьте содержимое. Замените домен и порт приложения:

server {

listen 80;

server_name app.example.com;

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;

}

}

Главная строка — proxy_pass: она говорит Nginx, куда передавать запросы. Отступы и пустые строки в конфиге Nginx не важны, важны только фигурные скобки и точка с запятой в конце каждой директивы.

Включите конфиг: в Ubuntu файлы лежат в sites-available, а работают только те, на которые есть ссылка в sites-enabled:

sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/

Проверьте синтаксис и примените изменения:

sudo nginx -t && sudo systemctl reload nginx

Всегда запускайте nginx -t перед перезагрузкой: если в конфиге ошибка, команда покажет файл и номер строки, а работающий Nginx не пострадает.

Откройте http://app.example.com — должно появиться ваше приложение.

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

Без них приложение видит все запросы так, будто они пришли от самого Nginx: с адреса 127.0.0.1, по HTTP и на хост 127.0.0.1:3000. Заголовки передают приложению настоящие данные посетителя:

  • Host $host — домен, который запросил посетитель. Без него приложение строит неправильные ссылки и редиректы на 127.0.0.1:3000.
  • X-Real-IP $remote_addr — реальный IP посетителя. Нужен для логов, лимитов и блокировок по IP внутри приложения.
  • X-Forwarded-For $proxy_add_x_forwarded_for — цепочка IP, через которые прошёл запрос. Большинство фреймворков берут IP посетителя именно отсюда.
  • X-Forwarded-Proto $scheme — протокол, по которому пришёл посетитель: http или https. Без него приложение не знает про HTTPS, и возникают бесконечные редиректы или не работают cookie для входа.

Приложение должно доверять прокси. Многие фреймворки по умолчанию игнорируют эти заголовки из соображений безопасности. В Express это включается строкой app.set('trust proxy', 1), в Django — настройкой SECURE_PROXY_SSL_HEADER, в n8n — переменной N8N_PROXY_HOPS=1. Проверьте документацию своего фреймворка по словам «behind a proxy».

Чтобы не повторять заголовки в каждом конфиге, вынесите их в отдельный файл /etc/nginx/snippets/proxy-headers.conf и подключайте одной строкой внутри location:

include snippets/proxy-headers.conf;

Создайте этот файл командой sudo nano /etc/nginx/snippets/proxy-headers.conf и вставьте в него четыре строки proxy_set_header из шага 2. Пример с location /api/ ниже использует именно его: без файла nginx -t выдаст ошибку.

Шаг 3. Включите HTTPS

Получите бесплатный сертификат Let's Encrypt через Certbot:

sudo apt install -y certbot python3-certbot-nginx

sudo certbot --nginx -d app.example.com

Certbot сам допишет в конфиг настройки SSL, включит перенаправление с HTTP на HTTPS и настроит автопродление. Блок location с proxy_pass он не трогает.

Откройте https://app.example.com — приложение должно работать с замком в адресной строке. Подробно о сертификатах, проверке автопродления и wildcard — в статье как установить SSL-сертификат Let's Encrypt на Nginx.

Как проксировать WebSocket

WebSocket — это постоянное соединение между браузером и сервером. Его используют чаты, панели с обновлением в реальном времени, редактор n8n, многие современные веб-приложения. По умолчанию Nginx такие соединения не пропускает.

Признаки проблемы: приложение открывается, но пишет «Connection lost» или «Disconnected», данные не обновляются без перезагрузки страницы, в консоли браузера ошибки WebSocket connection failed.

Добавьте в блок location три строки:

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection "upgrade";

Примените изменения: sudo nginx -t && sudo systemctl reload nginx.

Если WebSocket-соединение обрывается ровно через минуту простоя, увеличьте таймаут: proxy_read_timeout 3600s;. По умолчанию Nginx закрывает соединение, если 60 секунд по нему ничего не передавалось.

Несколько сервисов на одном сервере

Способ 1: по поддоменам (рекомендуем). Каждому сервису — свой поддомен и свой файл конфигурации. Повторите шаги 2 и 3 для каждого, меняя server_name и порт в proxy_pass:

Для каждого поддомена нужна A-запись в DNS. Этот способ работает с любым приложением без дополнительной настройки.

Способ 2: по путям на одном домене. Например, сайт на example.com, а API на example.com/api/. Добавьте в блок server второй location:

location /api/ {

proxy_pass http://127.0.0.1:8000/;

include snippets/proxy-headers.conf;

}

Ловушка со слешем в конце proxy_pass. От одного символа зависит, какой путь получит приложение:

  • proxy_pass http://127.0.0.1:8000/; (со слешем) — Nginx отрезает префикс. Запрос /api/users приходит в приложение как /users.
  • proxy_pass http://127.0.0.1:8000; (без слеша) — путь передаётся как есть. Запрос /api/users приходит как /api/users.

Выбирайте вариант по тому, какие пути ожидает ваше приложение. Ошибка 404 на всех запросах после настройки почти всегда означает не тот вариант.

Учтите, что готовые веб-приложения с интерфейсом часто не работают в подпапке: стили и скрипты запрашиваются от корня домена и не находятся. Для них берите поддомен.

Таймауты, загрузка файлов и потоковые ответы

Настройки по умолчанию подходят обычному сайту, но мешают приложениям с долгими запросами и загрузкой файлов. Все директивы ниже добавляются в блок location или server.

  • Размер загружаемых файлов. По умолчанию Nginx принимает запросы не больше 1 МБ и на большие файлы отвечает ошибкой 413. Увеличьте лимит: client_max_body_size 50m;
  • Долгие запросы. Если приложение отвечает дольше 60 секунд (отчёты, импорт, запросы к нейросетям), Nginx обрывает соединение с ошибкой 504. Увеличьте ожидание: proxy_read_timeout 300s; и proxy_send_timeout 300s;
  • Потоковые ответы. Nginx накапливает ответ приложения в буфере и только потом отдаёт посетителю. Для Server-Sent Events, стриминга ответов нейросетей и прогресс-баров буферизацию нужно отключить: proxy_buffering off;
  • Сжатие. В Ubuntu gzip для HTML уже включён. Чтобы сжимались и JSON, CSS и JavaScript, раскомментируйте строку gzip_types в /etc/nginx/nginx.conf.

После любых изменений: sudo nginx -t && sudo systemctl reload nginx.

Как защитить приложение за прокси

Закройте прямой доступ к порту приложения. Если приложение слушает 0.0.0.0:3000, оно доступно из интернета по адресу http://IP_СЕРВЕРА:3000 в обход Nginx: без HTTPS, лимитов и пароля. Настройте приложение слушать только 127.0.0.1. Для Docker публикуйте порт так: -p 127.0.0.1:3000:3000. Это особенно важно, потому что Docker открывает порты в обход UFW — подробнее в статье как установить Docker на Ubuntu.

Проверка: sudo ss -tlnp | grep 3000 должен показывать 127.0.0.1:3000, а не 0.0.0.0:3000 или *:3000.

Вход по паролю. Если у приложения нет своей авторизации (панель мониторинга, тестовый стенд), закройте его паролем на уровне Nginx. Создайте файл с пользователем:

sudo apt install -y apache2-utils

sudo htpasswd -c /etc/nginx/.htpasswd admin

Добавьте в блок location две строки:

auth_basic "Restricted";

auth_basic_user_file /etc/nginx/.htpasswd;

Используйте такой пароль только вместе с HTTPS, иначе он передаётся по сети в открытом виде.

Доступ только с своих IP. Для админки или внутреннего сервиса добавьте в location:

allow 203.0.113.10;

deny all;

Лимит запросов. Защищает от перебора паролей и простых атак. Сначала опишите зону лимита (10 запросов в секунду с одного IP):

echo 'limit_req_zone $binary_remote_addr zone=app:10m rate=10r/s;' | sudo tee /etc/nginx/conf.d/limits.conf

Затем включите её в блоке location:

limit_req zone=app burst=20 nodelay;

Превысившие лимит запросы получат ошибку 503. Для API с частыми запросами поднимите rate и burst. Больше о защите от атак — в статье как защитить VPS от DDoS-атак.

Частые ошибки

Причину любой ошибки Nginx пишет в лог: sudo tail -n 50 /var/log/nginx/error.log. Начинайте диагностику с него.

  • 502 Bad Gateway. Nginx не может подключиться к приложению: оно не запущено, упало или слушает другой порт. Проверьте curl -I http://127.0.0.1:3000 и порт в proxy_pass.
  • 504 Gateway Timeout. Приложение отвечает дольше 60 секунд. Увеличьте proxy_read_timeout или разберитесь, почему запрос выполняется так долго.
  • 413 Request Entity Too Large. Файл больше лимита. Увеличьте client_max_body_size.
  • 404 на всех страницах при проксировании по пути. Не тот вариант со слешем в конце proxy_pass. См. раздел про несколько сервисов.
  • Бесконечные редиректы или «смешанное содержимое» после включения HTTPS. Приложение не знает, что посетитель пришёл по HTTPS. Проверьте заголовок X-Forwarded-Proto и настройку доверия прокси в приложении.
  • В логах приложения у всех посетителей IP 127.0.0.1. Не передаются заголовки X-Real-IP и X-Forwarded-For, или приложение им не доверяет.
  • Открывается страница «Welcome to nginx!» вместо приложения. Домен в server_name не совпадает с адресом в браузере, или конфиг не включён ссылкой в sites-enabled.

Чек-лист

Частые вопросы

Чем reverse proxy отличается от обычного прокси? Обычный прокси стоит на стороне пользователя и скрывает его от сайтов. Reverse proxy стоит на стороне сервера и скрывает приложения от посетителей.

Нужен ли reverse proxy, если приложение одно? Да. Он даёт HTTPS, стандартные порты 80 и 443 без запуска приложения от root, лимиты и защиту от медленных соединений.

Можно ли проксировать на другой сервер? Да, укажите его адрес: proxy_pass http://10.0.0.5:3000;. Используйте приватную сеть и разрешите на втором сервере доступ к порту только с IP прокси.

Что выбрать: Nginx, Caddy или Traefik? Nginx — самый распространённый, по нему больше всего инструкций. Caddy проще и сам получает сертификаты. Traefik удобен, когда сервисы в Docker часто меняются. Для большинства задач на VPS хватает Nginx.

Сколько сервисов можно разместить за одним Nginx? Сколько угодно: сам Nginx потребляет несколько мегабайт памяти. Ограничение — только ресурсы сервера, которые нужны самим приложениям.