MAATRIX / Блог / Nginx как реверс-прокси на сервере: частые ошибки и решения

Nginx как реверс-прокси на сервере: частые ошибки и решения

Nginx как реверс-прокси на сервере: частые ошибки и решения

MAATRIX

Nginx как реверс-прокси на сервере обычно ставят за десять минут, а потом неделю ловят загадочные 502, обрывы загрузок и бесконечные редиректы. Хорошая новость: почти все ошибки типовые, у каждой понятная причина и короткое решение. Ниже — разбор самых частых проблем с командами диагностики, чтобы вы чинили по симптому, а не гадали.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

С чего начинать диагностику любой ошибки

Первое правило: не гадать, а смотреть логи. У Nginx их два, и они отвечают на разные вопросы. access.log показывает, какой код вернулся клиенту, error.log — почему. Держите оба открытыми, пока воспроизводите проблему:

tail -f /var/log/nginx/error.log /var/log/nginx/access.log

Второе правило: проверяйте конфиг перед каждым перезапуском. Команда nginx -t ловит опечатки и неверные пути до того, как они уронят рабочий сервер. Если тест прошёл, применяйте изменения через reload, а не restart — reload не рвёт активные соединения:

nginx -t && systemctl reload nginx

Третье правило: отделяйте проблему прокси от проблемы бэкенда. Если приложение не отвечает и напрямую (curl http://127.0.0.1:3000 с самого сервера), то Nginx ни при чём — чините бэкенд. Это простое разделение экономит часы: вы точно знаете, на чьей стороне сбой, и не правите конфиг прокси, когда виновато приложение.

Ошибка 502 Bad Gateway

Самая частая ошибка реверс-прокси. Она означает одно: Nginx не смог достучаться до бэкенда. В error.log при этом обычно видно connect() failed (111: Connection refused). Причин у неё несколько, и все проверяются быстро.

Чаще всего приложение просто не запущено или упало. Проверьте, слушает ли оно свой порт:

ss -tlnp | grep 3000
systemctl status myapp

Вторая причина — приложение слушает не тот интерфейс. Если в конфиге прокси стоит proxy_pass http://127.0.0.1:3000, а приложение подняли на 0.0.0.0:3000 внутри контейнера с другим сетевым пространством, соединения не будет. Убедитесь, что адрес и порт в proxy_pass совпадают с тем, что реально слушает бэкенд. Для Docker это часто означает обращение по имени сервиса или к адресу контейнера, а не к localhost хоста.

Третья причина — SELinux или AppArmor блокируют исходящее соединение Nginx. На системах с SELinux это лечится разрешением сетевых подключений для веб-сервера. Если 502 появляется только под нагрузкой, дело может быть в нехватке воркеров бэкенда — прокси исправен, а приложение не успевает принимать соединения, и здесь помогает более мощный сервер или больше процессов приложения.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Ошибка 504 Gateway Timeout

504 отличается от 502: соединение с бэкендом установилось, но ответа Nginx не дождался. Симптом — страница висит, потом отдаёт 504 ровно через 60 секунд. Это значение таймаута по умолчанию.

Если операция действительно долгая (генерация отчёта, тяжёлый запрос к базе), поднимите таймауты для нужного location:

proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;

Но прежде чем задирать таймауты, честно спросите себя, почему бэкенд отвечает минутами. Часто настоящая причина — не прокси, а медленный запрос к базе или нехватка памяти на сервере, из-за которой приложение уходит в своп. Увеличенный таймаут маскирует симптом, но пользователь всё равно ждёт полминуты. Если долгие ответы — норма для вашей задачи, это честный повод взять сервер помощнее с большим объёмом RAM и быстрым NVMe-диском, чтобы запросы укладывались в секунды.

Битые заголовки и потерянный IP клиента

Классическая жалоба: в логах приложения у всех посетителей один и тот же IP — адрес самого прокси. Причина в том, что забыли пробросить заголовки. Реверс-прокси обязан явно передавать исходные данные клиента:

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;

Без Host приложение видит неправильный домен и может строить кривые ссылки или отдавать не тот виртуальный хост. Без X-Forwarded-Proto фреймворк думает, что запрос пришёл по HTTP, хотя снаружи был HTTPS, и генерирует небезопасные ссылки. А приложение, в свою очередь, должно доверять этим заголовкам — во многих фреймворках для этого включают режим работы за прокси, иначе они игнорируют X-Forwarded-* из соображений безопасности.

Бесконечный редирект и ошибка 301/302 циклом

Браузер пишет «слишком много перенаправлений», страница не открывается. Почти всегда это конфликт представлений о протоколе. Nginx терминирует HTTPS и ходит на бэкенд по обычному HTTP. Приложение видит HTTP, считает соединение незащищённым и отвечает редиректом на HTTPS. Nginx снова расшифровывает, снова идёт по HTTP — и так по кругу.

Лечится это двумя согласованными действиями. Во-первых, передавайте бэкенду X-Forwarded-Proto $scheme. Во-вторых, настройте приложение доверять этому заголовку и считать соединение защищённым, когда там https. После этого цикл разрывается: приложение понимает, что снаружи уже HTTPS, и перестаёт слать лишние редиректы. Если редирект-цикл возник после подключения Cloudflare, проверьте ещё и режим SSL в его панели — «Flexible» как раз создаёт такую петлю.

WebSocket рвётся и не апгрейдится

Приложение с живыми обновлениями подключается и тут же отваливается, в консоли браузера — ошибка соединения WebSocket. Причина в том, что стандартный конфиг проксирования не пробрасывает заголовки апгрейда протокола. Для WebSocket нужен отдельный набор директив:

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_set_header Host $host;
    proxy_read_timeout 3600s;
}

Два ключевых момента здесь — proxy_http_version 1.1 (по умолчанию Nginx ходит на бэкенд по 1.0, где апгрейда нет) и заголовки Upgrade/Connection. Ещё частая ловушка — короткий proxy_read_timeout: тихое WebSocket-соединение без сообщений закрывается по таймауту, поэтому для долгих сокетов его поднимают до часа и больше.

Профилактика: как больше не возвращаться к этим ошибкам

Большинство описанных проблем не повторяются, если один раз собрать аккуратный шаблон конфига и переиспользовать его. Вынесите общий набор proxy_set_header в отдельный файл и подключайте его директивой include во всех location — так вы физически не сможете забыть заголовок в новом сайте.

Настройте мониторинг кодов ответа: рост доли 502 и 504 в access.log — ранний сигнал, что бэкенд начал захлёбываться. Простая проверка раз в минуту избавит от ситуации, когда о падении узнаёшь от пользователей. Держите систему и сам Nginx обновлёнными, а порты — закрытыми фаерволом, оставив снаружи только 80, 443 и SSH.

И помните про ресурсы. Значительная часть «ошибок Nginx» на деле — это нехватка памяти или процессора под нагрузкой, когда бэкенд не справляется, а прокси лишь честно отдаёт 502. Если сервер стабильно упирается в потолок, надёжнее перенести проект на VPS с запасом ресурсов и чистым IP — у MAATRIX это можно сделать в локациях RU, US или UK с оплатой из России картой или криптой, не подбирая иностранных платёжных костылей.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

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

Почему Nginx как реверс-прокси отдаёт 502 сразу после запуска?

Чаще всего бэкенд не поднят или слушает другой адрес: проверьте ss -tlnp и совпадение порта в proxy_pass с реальным портом приложения.

Как отличить ошибку прокси от ошибки приложения?

Обратитесь к бэкенду напрямую с сервера через curl http://127.0.0.1:PORT: если и там сбой, виновато приложение, а не Nginx.

Из-за чего возникает бесконечный редирект на HTTPS?

Приложение не знает, что снаружи уже HTTPS: передайте X-Forwarded-Proto и настройте бэкенд доверять этому заголовку.

Что делать, если 504 появляется под нагрузкой?

Это признак, что бэкенду не хватает ресурсов; поднимать таймауты — полумера, надёжнее взять сервер с большим объёмом RAM и быстрым диском.

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.