Как настроить 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:
app.example.com→proxy_passhttp://127.0.0.1:3000;n8n.example.com→proxy_passhttp://127.0.0.1:5678;api.example.com→proxy_passhttp://127.0.0.1:8000;
Для каждого поддомена нужна 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_passhttp://127.0.0.1:8000/;(со слешем) — Nginx отрезает префикс. Запрос/api/usersприходит в приложение как/users.proxy_passhttp://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 -Ihttp://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 потребляет несколько мегабайт памяти. Ограничение — только ресурсы сервера, которые нужны самим приложениям.