Nginx как reverse proxy на VPS: HTTPS, заголовки и диагностика 502
Приложение на VPS может исправно отвечать на локальном порту, например 127.0.0.1:3000, но оставаться недоступным по домену. Открывать этот порт напрямую в интернет обычно не требуется: перед приложением устанавливают Nginx, который принимает внешние HTTP- и HTTPS-запросы и передает их внутреннему сервису.
Такая схема позволяет привязать домен, подключить SSL-сертификат, скрыть внутренний порт приложения и централизованно управлять заголовками, журналами и ограничениями доступа. Она подходит для приложений на Node.js, Python, Go, Java и других платформах, а также для сервисов, запущенных в Docker.
Основные сложности начинаются не при установке Nginx, а на границе между ним и приложением. Неверный порт, неправильный адрес прослушивания, остановленная служба или ошибка Docker-сети приводят к 502 Bad Gateway. Если приложение принимает запрос, но слишком долго не отправляет данные, пользователь может получить 504 Gateway Timeout.
Ниже разберем базовую схему reverse proxy на Linux VPS, подключение HTTPS и WebSocket, а затем пройдем последовательную диагностику 502 и 504. В примерах используются домен app.example.ru, приложение на порту 3000, Nginx на Ubuntu или Debian. Доменные имена, порты, имена служб и пути в командах нужно заменить значениями своей инфраструктуры.
Как Nginx работает в роли reverse proxy
Reverse proxy, или обратный прокси-сервер, принимает запрос от клиента от имени приложения. Пользователь обращается к https://app.example.ru, но само приложение может работать только на локальном адресе 127.0.0.1:3000 и не принимать прямые соединения из интернета.
Nginx выполняет несколько задач:
- принимает соединения на стандартных портах 80 и 443;
- выбирает конфигурацию по доменному имени;
- завершает TLS-соединение и расшифровывает HTTPS;
- передает запрос внутреннему приложению;
- добавляет служебные заголовки;
- получает ответ приложения и возвращает его клиенту;
- записывает запросы и ошибки в журналы.
Что такое upstream
В более сложной инфраструктуре upstream может состоять из нескольких экземпляров приложения. Тогда Nginx распределяет запросы между ними. В этой статье рассматривается базовый вариант с одним приложением.
В контексте ошибок Nginx слово upstream особенно важно. Сообщение upstream timed out означает, что проблема возникла при обмене между Nginx и приложением, а не между браузером и самим Nginx.
Как проходит запрос
Пользователь
│
▼
DNS: app.example.ru → публичный IP VPS
│
▼
Nginx: порты 80 и 443
│
▼
Приложение: 127.0.0.1:3000
│
▼
База данных или другие внутренние сервисыТакая архитектура не делает неисправное приложение работоспособным. До настройки reverse proxy нужно убедиться, что сервис отвечает локально.
Что проверить до настройки Nginx
Диагностика будет значительно проще, если сначала проверить DNS, приложение и сетевые порты по отдельности.
Домен направлен на правильный VPS
A-запись домена должна указывать на публичный IPv4-адрес сервера. Если используется IPv6, отдельно проверяют AAAA-запись.
Проверить результат можно с рабочего компьютера:
dig +short app.example.ruЕсли dig не установлен, можно использовать:
getent ahosts app.example.ruПолученный адрес должен совпадать с публичным IP нужного VPS. После изменения DNS обновление зависит от TTL и кешей рекурсивных DNS-серверов, поэтому новый адрес может появиться не одновременно у всех пользователей.
Подробная настройка записей разобрана в статье «Как направить домен на Linux VPS: A, AAAA, CNAME, TTL и проверка DNS».
Приложение отвечает на локальном порту
Выполните запрос непосредственно с VPS:
curl -v http://127.0.0.1:3000/Рабочим результатом не обязательно должен быть код 200 OK. Приложение может возвращать перенаправление, страницу авторизации или ответ API. Важно, чтобы соединение устанавливалось и сервис возвращал осмысленный HTTP-ответ.
Если появляется Connection refused, на указанном адресе и порту никто не принимает соединения. Если команда долго ждет ответа, приложение зависло, выполняет длительную операцию либо обращается к недоступной базе данных или другому внутреннему сервису.
На каком адресе слушает приложение
Проверьте слушающие TCP-порты:
sudo ss -ltnp | grep ':3000'В выводе может быть один из адресов:
127.0.0.1:3000— приложение доступно только внутри VPS;0.0.0.0:3000— приложение принимает IPv4-соединения на всех интерфейсах;[::]:3000— приложение слушает IPv6 и, в зависимости от настроек системы, может принимать IPv4;- адрес Docker-интерфейса — доступ зависит от контейнерной сети и публикации порта.
Если Nginx установлен на том же VPS, адрес 127.0.0.1:3000 обычно является безопасным и достаточным. Публиковать порт приложения на всех интерфейсах без необходимости не следует.
Служба запущена и не перезапускается
Для приложения, оформленного как служба systemd, проверьте состояние:
sudo systemctl status myapp --no-pagerЗамените myapp фактическим именем unit-файла. Последние записи журнала можно получить так:
sudo journalctl -u myapp -n 100 --no-pagerОбратите внимание на циклические перезапуски, ошибки подключения к базе данных, занятый порт, недоступные файлы конфигурации и отсутствие переменных окружения.
Для Docker-контейнера используйте:
docker psЗатем посмотрите последние записи журнала контейнера:
docker logs --tail 100 ИМЯ_КОНТЕЙНЕРАКакие порты должны быть доступны извне
Для стандартной схемы достаточно внешнего доступа к портам:
80/tcp— HTTP и проверка домена при выпуске сертификата;443/tcp— HTTPS.
Порт приложения, например 3000/tcp, можно оставить закрытым для внешней сети. Nginx будет обращаться к нему локально.
Проверьте состояние firewall:
sudo ufw status verboseБазовая конфигурация reverse proxy
Если Nginx еще не установлен, в Ubuntu или Debian его можно установить из системного репозитория:
sudo apt update
sudo apt install nginxПосле установки проверьте службу:
sudo systemctl status nginx --no-pagerНа Ubuntu и Debian конфигурации отдельных сайтов обычно хранятся в /etc/nginx/sites-available/, а включенные сайты представлены символическими ссылками в /etc/nginx/sites-enabled/.
Создание server block
Создайте файл:
sudo nano /etc/nginx/sites-available/app.example.ruДобавьте базовую конфигурацию:
server {
listen 80;
listen [::]:80;
server_name app.example.ru;
access_log /var/log/nginx/app.example.ru.access.log;
error_log /var/log/nginx/app.example.ru.error.log;
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 должен отправлять запросы. В примере приложение работает на том же сервере и доступно по адресу 127.0.0.1:3000.
Зачем передавать заголовки
Без явной настройки приложение может неправильно определить домен, адрес клиента и исходный протокол.
Host сообщает приложению домен, по которому пришел запрос:
proxy_set_header Host $host;X-Real-IP передает адрес клиента, который видит Nginx:
proxy_set_header X-Real-IP $remote_addr;X-Forwarded-For формирует цепочку адресов при прохождении запроса через несколько прокси:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;X-Forwarded-Proto сообщает, использовал ли клиент HTTP или HTTPS:
proxy_set_header X-Forwarded-Proto $scheme;Последний заголовок особенно важен для приложений, которые самостоятельно создают абсолютные ссылки или выполняют перенаправления. Если приложение не доверяет reverse proxy либо не обрабатывает X-Forwarded-Proto, может возникнуть цикл перенаправлений между HTTP и HTTPS.
Включение сайта
Создайте символическую ссылку:
sudo ln -s /etc/nginx/sites-available/app.example.ru /etc/nginx/sites-enabled/app.example.ruЕсли ссылка уже существует, повторно выполнять команду не нужно.
Перед применением конфигурации обязательно проверьте синтаксис:
sudo nginx -tПри успешной проверке Nginx выводит сообщения:
syntax is ok
test is successfulТолько после этого примените изменения:
sudo systemctl reload nginxReload перечитывает конфигурацию без жесткой остановки обслуживаемых соединений. Если новая конфигурация некорректна, предварительный nginx -t позволит обнаружить ошибку до ее применения.
Проверка без ожидания обновления DNS
Можно проверить выбор блока server непосредственно на VPS:
curl -I -H 'Host: app.example.ru' http://127.0.0.1/Команда обращается к локальному Nginx, но передает нужное доменное имя в заголовке Host. Так можно отделить ошибку конфигурации Nginx от проблемы с DNS.
HTTPS, WebSocket и тайм-ауты
После того как приложение стабильно открывается по HTTP, можно подключать HTTPS. Не следует одновременно искать ошибку локального upstream, настраивать DNS и выпускать сертификат: при поэтапной работе легче определить неисправный участок.
Где завершается TLS
В простом сценарии на одном VPS соединение между Nginx и приложением может использовать обычный HTTP через 127.0.0.1. Этот трафик не покидает сервер. Если upstream расположен на другом сервере или проходит через недоверенную сеть, нужно отдельно защищать и проверять соединение между Nginx и приложением.
После выпуска сертификата конфигурация может выглядеть так:
server {
listen 80;
listen [::]:80;
server_name app.example.ru;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name app.example.ru;
ssl_certificate /etc/letsencrypt/live/app.example.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.ru/privkey.pem;
access_log /var/log/nginx/app.example.ru.access.log;
error_log /var/log/nginx/app.example.ru.error.log;
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;
}
}Подробный выпуск и автоматическая проверка сертификатов рассматриваются в статье «Автоматическое продление SSL-сертификата: Certbot, systemd timer и проверка обновления».
Настройка WebSocket
Заголовки Upgrade и Connection относятся к конкретному сетевому участку и не передаются upstream автоматически. Для WebSocket их нужно установить явно.
Универсальный вариант начинается с блока map в контексте http. В Ubuntu и Debian его можно разместить, например, в отдельном файле /etc/nginx/conf.d/websocket-map.conf:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}После этого в нужном location добавляют:
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}Явная директива proxy_http_version 1.1 сохраняет совместимость с версиями Nginx, в которых для проксирования по умолчанию использовался HTTP/1.0.
После изменения снова выполните:
sudo nginx -tИ только после успешной проверки:
sudo systemctl reload nginxКак выбирать тайм-ауты
Для соединения с upstream используются разные тайм-ауты:
proxy_connect_timeout— время на установление соединения;proxy_send_timeout— допустимый перерыв между операциями передачи запроса upstream;proxy_read_timeout— допустимый перерыв между операциями чтения ответа upstream.
По умолчанию proxy_read_timeout составляет 60 секунд. Это не общий лимит длительности ответа: таймер учитывает промежуток между последовательными операциями чтения.
Если приложение выполняет длительную задачу и ничего не отправляет клиенту, Nginx может закрыть соединение. Увеличивать тайм-аут следует только после проверки поведения приложения.
Например:
location /long-operation/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 120s;
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;
}Это не универсальные значения. Если обычный запрос занимает две минуты, сначала нужно проверить приложение, базу данных и внешние зависимости. Большой тайм-аут может скрыть проблему, но не устранит ее.
Для WebSocket лучше также предусмотреть периодические ping-кадры со стороны приложения. Они поддерживают активность соединения и позволяют обнаруживать разрыв связи.
Почему появляются ошибки 502 и 504
Код 502 Bad Gateway означает, что Nginx не смог получить корректный ответ от upstream. Код 504 Gateway Timeout означает, что соединение или обмен с upstream не завершились за допустимое время.
Браузер показывает только итоговый код. Настоящая причина обычно содержится в /var/log/nginx/error.log, отдельном журнале виртуального хоста или журнале приложения.
| Симптом или запись в журнале | Вероятная причина | Что проверить |
|---|---|---|
connect() failed (111: Connection refused) | На адресе и порту никто не принимает соединение | Службу приложения, вывод ss, значение proxy_pass |
No route to host | Ошибка адреса, маршрута или контейнерной сети | IP upstream, Docker network, firewall |
host not found in upstream | Имя upstream не разрешается | DNS, имя Docker-сервиса, расположение Nginx |
upstream prematurely closed connection | Приложение закрыло соединение до полного ответа | Журнал приложения, лимиты памяти, сбой процесса |
upstream timed out | Приложение долго не передает данные | Базу данных, внешние API, нагрузку и тайм-ауты |
| 502 появляется после перезапуска приложения | Nginx доступен раньше upstream | Порядок запуска и состояние службы |
| WebSocket подключается и сразу разрывается | Не переданы Upgrade-заголовки или приложение не поддерживает маршрут | Конфигурацию location, журнал приложения |
| HTTP работает, а HTTPS зацикливается | Приложение не доверяет reverse proxy или не учитывает X-Forwarded-Proto | Настройки trusted proxy и редиректы приложения |
Приложение не запущено
Сначала проверьте upstream напрямую:
curl -v http://127.0.0.1:3000/Затем состояние службы:
sudo systemctl status myapp --no-pagerЕсли служба находится в состоянии failed, посмотрите ее журнал:
sudo journalctl -u myapp -n 100 --no-pagerПерезапуск приложения без изучения ошибки может временно скрыть причину. Перед восстановлением сервиса важно проверить, почему процесс завершился: нехватка памяти, ошибка конфигурации, недоступная база, занятый порт или поврежденный файл.
В Nginx указан неправильный адрес или порт
Сравните proxy_pass с выводом:
sudo ss -ltnpЕсли приложение слушает 127.0.0.1:3001, а Nginx обращается к 127.0.0.1:3000, результатом будет 502.
Отдельно учитывайте IPv4 и IPv6. Имя localhost может разрешаться в 127.0.0.1, ::1 или оба адреса. Если приложение слушает только IPv4, явный адрес 127.0.0.1 делает конфигурацию предсказуемее.
Ошибка сети Docker
Схема зависит от расположения Nginx.
Если Nginx установлен на хостовой системе, а приложение работает в контейнере, порт можно опубликовать только на loopback-интерфейсе:
ports:
- "127.0.0.1:3000:3000"Тогда хостовой Nginx обращается к:
proxy_pass http://127.0.0.1:3000;Приложение внутри контейнера обычно должно слушать 0.0.0.0:3000. Если оно привязано к 127.0.0.1 внутри контейнера, опубликованный порт может не обеспечить доступ к процессу.
Если и Nginx, и приложение работают в контейнерах одной пользовательской сети Docker, Nginx может обращаться к имени сервиса:
proxy_pass http://app:3000;Имя app разрешается только внутри соответствующей Docker-сети. Хостовой Nginx обычно не сможет использовать имя контейнерного сервиса напрямую.
Проверьте контейнеры и сети:
docker ps
docker network ls
docker inspect ИМЯ_КОНТЕЙНЕРАПриложение отвечает слишком долго
Ошибка 504 часто является следствием проблемы ниже по цепочке:
- медленного SQL-запроса;
- блокировки в базе данных;
- ожидания внешнего API;
- нехватки CPU или RAM;
- исчерпания пула соединений;
- зависшего фонового процесса;
- слишком долгой синхронной операции.
Проверьте журнал приложения в тот же момент времени, когда Nginx записал upstream timed out. Простое увеличение proxy_read_timeout оправдано только тогда, когда операция действительно должна выполняться долго и это предусмотрено архитектурой.
Для тяжелых задач лучше использовать асинхронную модель: приложение быстро принимает запрос, ставит задачу в очередь и позволяет клиенту отдельно получить ее статус.
Пошаговая диагностика и проверка результата
При 502 или 504 лучше двигаться от ближайшего к приложению уровня к внешнему клиенту. Так каждый шаг проверяет только один участок цепочки.
Шаг 1. Проверить upstream напрямую
curl -v http://127.0.0.1:3000/Если локального ответа нет, Nginx пока не является основной проблемой. Нужно восстановить приложение.
Шаг 2. Проверить слушающие порты
sudo ss -ltnp | grep ':3000'Убедитесь, что порт, адрес и процесс соответствуют конфигурации proxy_pass.
Шаг 3. Проверить службу или контейнер
Для systemd:
sudo systemctl status myapp --no-pager
sudo journalctl -u myapp -n 100 --no-pagerДля Docker:
docker ps
docker logs --tail 100 ИМЯ_КОНТЕЙНЕРАШаг 4. Проверить конфигурацию Nginx
sudo nginx -tЕсли проверка не проходит, Nginx укажет файл и строку, где обнаружена проблема. Исправьте синтаксис до reload.
Чтобы увидеть итоговую конфигурацию с учетом всех подключаемых файлов, можно использовать:
sudo nginx -TШаг 5. Посмотреть журнал ошибок Nginx
Для отдельного журнала виртуального хоста:
sudo tail -n 100 /var/log/nginx/app.example.ru.error.logЕсли отдельный журнал не настроен:
sudo tail -n 100 /var/log/nginx/error.logДля наблюдения во время повторного запроса:
sudo tail -f /var/log/nginx/app.example.ru.error.logЗавершить просмотр можно сочетанием Ctrl+C. Сопоставляйте время ошибки Nginx с журналом приложения. Это позволяет увидеть одну и ту же неудачную операцию с двух сторон.
Шаг 6. Проверить Nginx локально
curl -I -H 'Host: app.example.ru' http://127.0.0.1/Если локальный запрос работает, а внешний — нет, проверяйте DNS, firewall и доступность портов 80 и 443.
Шаг 7. Проверить внешний HTTP и HTTPS
curl -Iv http://app.example.ru/
curl -Iv https://app.example.ru/Проверьте:
- на какой IP устанавливается соединение;
- какой HTTP-код возвращается;
- есть ли перенаправление на HTTPS;
- соответствует ли сертификат домену;
- нет ли цикла перенаправлений;
- какой сервер формирует ошибку.
Шаг 8. Применить исправление безопасно
После изменения конфигурации:
sudo nginx -tЕсли проверка успешна:
sudo systemctl reload nginxЗатем повторите локальную и внешнюю проверку. Не ограничивайтесь тем, что главная страница открылась: проверьте авторизацию, API, загрузку файлов и WebSocket, если приложение их использует.
Рабочая конфигурация должна соответствовать следующим признакам:
- HTTP перенаправляется на HTTPS;
- сертификат соответствует домену;
- приложение отвечает через Nginx;
- в приложении корректно определяется исходный протокол;
- клиентский IP передается ожидаемым способом;
- WebSocket не разрывается сразу после подключения;
- внутренний порт не открыт в интернет без необходимости;
nginx -tзавершается успешно;- в журналах нет повторяющихся upstream-ошибок.
Заключение
Nginx как reverse proxy отделяет публичный HTTP/HTTPS-контур от внутреннего приложения. Пользователь подключается к стандартному порту 443, а приложение может оставаться на локальном адресе и не принимать прямые соединения из интернета.
Надежность этой схемы зависит не от количества директив, а от последовательной проверки каждого участка. Сначала нужно убедиться, что приложение отвечает напрямую. Затем проверить адрес и порт, настроить proxy_pass, передать необходимые заголовки и только после этого подключать HTTPS, WebSocket и специальные тайм-ауты.
Ошибка 502 обычно указывает на невозможность получить корректный ответ от upstream: приложение остановлено, указан неправильный порт или нарушена контейнерная сеть. Ошибка 504 чаще связана с длительным ожиданием ответа. В обоих случаях основным источником фактов становятся журналы Nginx и приложения, сопоставленные по времени.
Что почитать дальше
Если вы разворачиваете приложение на Linux VPS, пригодятся связанные инструкции:
- Docker на Linux VPS: production-развертывание, сеть, тома, HTTPS, обновления и резервное копирование — как организовать постоянную работу контейнерного приложения.
- Как направить домен на Linux VPS: A, AAAA, CNAME, TTL и проверка DNS — как связать домен с публичным адресом сервера.
- Автоматическое продление SSL-сертификата: Certbot, systemd timer и проверка обновления — как контролировать продление сертификата.
- Мониторинг Linux VPS: CPU, RAM, диск, systemd, логи и базовые уведомления — как следить за Nginx, приложением и ресурсами VPS.
Когда стоит обратиться к специалистам
Базовую схему с одним приложением часто можно настроить самостоятельно. Помощь специалиста особенно полезна, если приложение критично для работы компании, на сервере размещено несколько доменов, ошибка 502 возникает периодически или требуется подключить HTTPS без длительного простоя.
Для размещения веб-приложения можно выбрать Linux VPS IPWAY. Для проектов на системах семейства Debian доступны отдельные конфигурации VPS на Ubuntu и VPS Debian.
Кратко
- Nginx принимает внешние HTTP/HTTPS-запросы и передает их внутреннему приложению.
- Перед настройкой proxy нужно проверить приложение напрямую через
curl. - Адрес и порт в
proxy_passдолжны совпадать с фактической точкой прослушивания upstream. - Заголовки
Host,X-Real-IP,X-Forwarded-ForиX-Forwarded-Protoпомогают приложению правильно определить исходный запрос. - Для WebSocket заголовки
UpgradeиConnectionпередаются явно. - Ошибка 502 обычно означает, что Nginx не получил корректный ответ от приложения.
- Ошибка 504 чаще указывает на слишком долгое ожидание данных от upstream.
- Перед каждым reload нужно выполнять
nginx -tи проверять результат после применения конфигурации.