Проксирование в NGINX через proxy_pass без сюрпризов
Директива proxy_pass связывает NGINX с приложением, но один символ в конце URL меняет путь, который получит бэкенд. Разбираем конфигурацию, заголовки, WebSocket, таймауты и типичные ошибки на NGINX 1.25+.

Одна и та же директива proxy_pass может отправить запрос /app/users в приложение как /users или как /app/users — всё зависит от завершающего слеша в конфигурации. Ошибка проявляется не сразу: главная страница открывается, а статические файлы, API или авторизация начинают отдавать 404 и 502. Ниже — рабочая схема для NGINX Open Source 1.25+ и критерии, по которым можно выбрать правильный вариант.
Что делает proxy_pass
proxy_pass превращает NGINX в обратный прокси: клиент подключается к NGINX, а тот сам устанавливает соединение с приложением и передаёт ему запрос. Приложением может быть сервис на Node.js, Python, Go, PHP через отдельный HTTP-сервер или любой другой процесс, который слушает TCP-порт.
Минимальная конфигурация выглядит так:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
После изменения файла проверьте синтаксис командой nginx -t, затем перечитайте конфигурацию без остановки рабочих соединений командой nginx -s reload. Команды требуют прав, с которыми запущен NGINX, часто это sudo. Проверка результата — приложение отвечает через доменное имя, а в его журнале появляется входящий запрос.
Сноска. Обратный прокси — сервер-посредник, который принимает запрос от клиента и обращается к внутреннему приложению от своего имени. Клиент не видит внутренний порт и обычно не знает, где физически запущен сервис.
Главная ловушка: слеш после порта
Важна не только доступность upstream, но и форма URL в proxy_pass. Рассмотрим запрос к /app/users:
| Конфигурация | Что обычно получает приложение | Когда использовать |
|---|---|---|
proxy_pass http://127.0.0.1:3000; |
/app/users |
Приложение само знает о префиксе /app |
proxy_pass http://127.0.0.1:3000/; |
/users |
Префикс /app/ нужно убрать перед отправкой |
proxy_pass http://127.0.0.1:3000/api/; |
Путь пересобирается относительно указанного URI | Внутренний сервис должен получать отдельный префикс |
Типичная конфигурация для приложения, которое слушает корень API:
server {
listen 80;
server_name example.com;
location /app/ {
proxy_pass http://127.0.0.1:3000/;
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;
}
}
Здесь запрос /app/users?id=42 уйдёт в приложение как /users?id=42. Если приложение ожидает путь /app/users, уберите слеш после порта и оставьте proxy_pass http://127.0.0.1:3000;.
У
proxy_passслеш — часть логики маршрутизации, а не косметика форматирования.
Заголовки, которые стоит передать приложению
Без явной настройки приложение может увидеть адресом клиента сам NGINX, а не реальный IP пользователя. Заголовок Host тоже стоит задавать явно: он нужен для виртуальных хостов, генерации ссылок и проверки домена.
Host $host— доменное имя из запроса без случайной подмены порта;X-Real-IP $remote_addr— адрес непосредственного клиента для приложения;X-Forwarded-For $proxy_add_x_forwarded_for— цепочка адресов через прокси;X-Forwarded-Proto $scheme— исходная схема HTTP или HTTPS.
Эти значения нельзя бездумно использовать для авторизации. Если NGINX доступен напрямую из интернета, приложение должно доверять заголовкам X-Forwarded-* только от известного прокси или внутренней сети. Иначе клиент сможет подставить ложный IP в необработанной конфигурации.
Когда нужны таймауты и размеры буфера
По умолчанию запрос может ждать ответ приложения ограниченное время, а медленный сервис начнёт возвращать ошибку прокси. Таймауты задают под конкретный сценарий, а не копируют из случайного примера:
location /app/ {
proxy_pass http://127.0.0.1:3000/;
proxy_connect_timeout 3s;
proxy_send_timeout 30s;
proxy_read_timeout 60s;
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_connect_timeout ограничивает установление соединения с upstream, proxy_send_timeout — передачу запроса, а proxy_read_timeout — ожидание данных от приложения между операциями чтения. Увеличение последнего параметра не делает сервис быстрее: оно лишь дольше удерживает соединение и может замаскировать зависание.
WebSocket: отдельные заголовки
Обычный HTTP-проксинг и WebSocket используют разные этапы установки соединения. Для WebSocket нужно передать заголовки Upgrade и Connection. Переменную лучше объявить в контексте http, чтобы не отправлять Connection: upgrade всем обычным запросам.
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name example.com;
location /socket/ {
proxy_pass http://127.0.0.1:3000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 60m;
}
}
}
После reload проверьте не только HTTP-ответ, но и реальное открытие WebSocket из клиента. Если соединение сразу закрывается, смотрите журнал NGINX и логи приложения: причиной может быть путь, Origin, авторизация или таймаут, а не только proxy_pass.
Что ломается чаще всего
502 Bad Gateway
NGINX не смог получить ответ от upstream. Проверьте, запущено ли приложение, слушает ли оно именно 127.0.0.1:3000, совпадает ли адрес IPv4 и IPv6, а также не блокирует ли соединение локальный firewall. В журнале ошибок обычно есть причина: connection refused, no route to host или таймаут.
404 после добавления префикса
Сначала выведите в лог приложения фактический путь. Если сервис ждёт /users, а получает /app/users, нужен слеш в конце URL proxy_pass и совпадающий location /app/. Если сервис, наоборот, ожидает полный префикс, слеш после порта убирать нельзя.
Бесконечный редирект на HTTPS
Приложение может считать запрос HTTP, хотя клиент пришёл по HTTPS. Передайте X-Forwarded-Proto $scheme и настройте само приложение доверять этому заголовку только от NGINX. Простое добавление заголовка не исправит ошибку, если фреймворк игнорирует прокси по соображениям безопасности.
В приложении виден адрес NGINX
Проверьте X-Real-IP и X-Forwarded-For, а затем настройку доверенных прокси в приложении. Для цепочки из нескольких прокси важно не заменять список адресов значением $remote_addr, иначе вы потеряете предыдущие адреса.
Как проверять конфигурацию перед продом
- Проверьте синтаксис командой
nginx -t. В успешном выводе должны быть сообщения о корректном синтаксисе и успешной проверке конфигурации. - Сделайте запрос к NGINX с нужным путём и параметрами. Сравните URL в access-логе NGINX и в журнале приложения.
- Проверьте ответы 200, 3xx, 4xx и 5xx отдельно. Редирект и ошибка авторизации могут быть корректным поведением приложения.
- Перезагрузите конфигурацию через
nginx -s reloadи повторите проверку. Старые рабочие соединения не должны обрываться только из-за reload. - Для WebSocket, загрузки файлов и длинных запросов проведите отдельный тест. Обычный запрос главной страницы эти сценарии не проверяет.
Измеряйте не только код ответа, но и время до первого байта, полное время ответа, число 502 и длительность соединений. Если проблема появляется под нагрузкой, сопоставьте эти показатели с количеством процессов приложения, очередью запросов и лимитами операционной системы. Универсальных значений таймаутов и числа workers нет: их подбирают по профилю конкретного сервиса.
Что настроить дальше
После базового проксирования добавьте мониторинг 4xx, 5xx и времени ответа upstream, а затем проверьте graceful reload на копии боевой конфигурации. Следующий отдельный этап — TLS в NGINX и корректная политика доверия к заголовкам X-Forwarded-*. Инвалидация кеша, балансировка между несколькими upstream и ограничение запросов — самостоятельные темы, их не стоит прятать в один универсальный блок.
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: NGINX ngx_http_proxy_module: proxy_pass и связанные директивы, Официальное руководство NGINX для начинающих
Частые вопросы
Почему proxy_pass с завершающим слешем меняет URL?
Что означает ошибка 502 Bad Gateway?
Нужно ли всегда добавлять X-Forwarded-For и X-Forwarded-Proto?
Почему после проксирования приложение делает бесконечный редирект на HTTPS?
Что делать, если у меня другая версия NGINX?
Почему WebSocket закрывается сразу после подключения?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.