Введение: системный подход к отладке маршрутизации
Проблемы с маршрутизацией в Nginx проявляются одинаково: запрос уходит не на тот бэкенд, возвращается 404, зацикливается редирект или теряется часть URI при проксировании. Причина всегда одна - сервер выбрал не тот location-блок, который вы ожидали, либо передал запрос дальше с искажением. Быстрый способ вернуть контроль - включить детальное логгирование, пометить каждый location маркером через модуль echo и прогнать запросы через curl. Дальше подключается ngxtop для наблюдения за потоком трафика в реальном времени.
Эта статья даёт пошаговый алгоритм диагностики. Сначала настраиваем логи так, чтобы видеть полный путь запроса. Затем проверяем фактический выбор location. После этого разбираем типичные ошибки в proxy_pass и rewrite. В конце - чек-лист, который сокращает время поиска сбоя с часов до минут. Материал рассчитан на DevOps-инженеров и системных администраторов, работающих с продакшен-конфигурациями.
Если вы только выстраиваете схему маршрутизации, начните с руководства по настройке Nginx как L7-маршрутизатора. Здесь же фокус на диагностике уже работающей или сломанной конфигурации.
Настройка детального логгирования для диагностики
Дефолтный формат access_log показывает минимум: IP, дату, метод, URI, код ответа. Для отладки маршрутизации этого недостаточно. Нужно видеть, какой апстрим фактически обработал запрос, сколько времени это заняло и какие заголовки передавались. Настройка собственного log_format решает задачу.
Конфигурация access_log: какие данные важны
Добавьте в блок http следующий формат:
log_format debug_fmt '$remote_addr - [$time_local] "$request" '
'status=$status bytes=$body_bytes_sent '
'uri=$request_uri upstream=$upstream_addr '
'rt=$request_time uht=$upstream_header_time '
'referer=$http_referer ua=$http_user_agent';
access_log /var/log/nginx/access.log debug_fmt;Переменная $upstream_addr показывает адрес бэкенда, выбранного для проксирования. Если запрос ушёл не на тот сервер, вы увидите это сразу. $request_time и $upstream_header_time помогают отличить медленный бэкенд от медленной маршрутизации. $request_uri фиксирует исходный URI до любых rewrite-преобразований, что критично для диагностики искажений пути.
При ротации логов используйте отдельный файл для отладочного формата, чтобы не смешивать его с обычным трафиком. Для продакшена такой детальный формат включайте временно, на период диагностики, затем возвращайте стандартный.
Использование error_log для выявления ошибок маршрутизации
Уровни логирования в error_log: debug, info, notice, warn, error, crit. Для отладки маршрутизации включайте debug точечно, чтобы не завалить диск записями. Это делается так:
server {
listen 80;
server_name example.com;
error_log /var/log/nginx/debug.log debug;
location /api/ {
error_log /var/log/nginx/api-debug.log debug;
proxy_pass http://backend;
}
}В debug-логе Nginx пишет весь процесс выбора location: какие блоки проверялись, какой модификатор совпал, какие rewrite-правила применились. Типичные записи, на которые стоит обращать внимание: using configuration, test location, rewritten data. Ошибки вида upstream timed out или connect() failed указывают на проблемы с бэкендом, а не с маршрутизацией.
Детальный разбор логов с готовыми командами grep и awk есть в материале по практическому анализу логов Nginx и Apache.
Проверка выбора location с помощью модуля echo
Самый быстрый способ понять, какой location обработал запрос, - добавить в каждый блок директиву echo с уникальным маркером. Модуль ngx_http_echo_module выводит строку прямо в тело ответа. Запрос через curl покажет маркер, и вы мгновенно увидите фактический выбор.
Установка и настройка модуля echo
В Debian и Ubuntu модуль входит в пакет nginx-extras. Проверьте наличие:
nginx -V 2>&1 | grep -o http_echo_moduleЕсли вывода нет, установите расширенную сборку:
apt install nginx-extrasДля CentOS и сборок из исходников модуль компилируется отдельно. Подробности подключения сторонних модулей без риска для продакшена разобраны в гиде по модулям Nginx.
Практический пример: добавление echo в location
Конфигурация с маркерами:
location / {
echo 'MATCH: root location';
}
location /api/ {
echo 'MATCH: api location';
}
location ~ \.php$ {
echo 'MATCH: php regex location';
}Проверка:
curl http://example.com/api/users
# Ответ: MATCH: api location
curl http://example.com/index.php
# Ответ: MATCH: php regex locationМетод временный. Перед выкладкой в продакшен маркеры удаляйте, иначе реальные ответы приложения подменятся отладочными строками.
Использование curl для тестирования маршрутизации
curl - основной инструмент проверки. Он показывает заголовки, коды ответа, цепочки редиректов и позволяет отправлять произвольные заголовки. Три опции закрывают 90% задач диагностики: -I, -L, -v.
Проверка заголовков и кодов ответа
Команда curl -I отправляет HEAD-запрос и выводит только заголовки:
curl -I http://example.com/api/healthВ ответе смотрите: HTTP/2 200 или HTTP/1.1 404, заголовок Server, Location при редиректах, кастомные X-* заголовки, которые вы добавляли для отладки. Если бэкенд возвращает свой Server, а вы ожидали Nginx, значит запрос проксируется без подмены заголовков - это нормально, но полезно знать при диагностике.
Для проверки конкретного location с передачей заголовков:
curl -I -H "X-Debug: 1" http://example.com/api/usersОтслеживание редиректов с помощью curl -L
Цепочку редиректов показывает опция -L с выводом URL на каждом шаге:
curl -L -w '%{url_effective}\n' -o /dev/null http://example.com/old-pathЕсли URL повторяется или цепочка длиннее трёх переходов, вероятен цикл. Nginx в таких случаях возвращает ошибку ERR_TOO_MANY_REDIRECTS в браузере или обрывает соединение. Причина обычно в конфликте rewrite-правил: правило срабатывает повторно на уже изменённом URI. Подробный вывод curl -v покажет каждый запрос и ответ с заголовком Location.
Мониторинг трафика в реальном времени с ngxtop
ngxtop парсит access-лог и выводит агрегированную статистику в стиле top: самые частые запросы, распределение кодов ответа, топ IP-адресов. Инструмент полезен, когда нужно увидеть картину в целом, а не разбирать одиночный запрос.
Установка и базовое использование ngxtop
Установка через pip:
pip install ngxtopЗапуск с указанием файла лога:
ngxtop -l /var/log/nginx/access.logБез аргументов ngxtop попытается найти лог автоматически. В выводе по умолчанию: количество запросов, процент от общего числа, метод, URI, код ответа. Для кастомного формата лога укажите его через опцию --format, иначе парсер не распознает поля.
Анализ проблемных запросов с помощью ngxtop
Фильтр по ошибкам 4xx и 5xx:
ngxtop -l /var/log/nginx/access.log -i 'status >= 400'Группировка по URI для поиска самых проблемных эндпоинтов:
ngxtop -l /var/log/nginx/access.log -g 'request_uri'Топ медленных запросов по времени ответа апстрима:
ngxtop -l /var/log/nginx/access.log -g 'upstream_addr' --filter 'upstream_response_time > 1'Если в логе нет нужных полей, сначала добавьте их в log_format, как показано выше. ngxtop работает только с тем, что записано в файл.
Типичные ошибки конфигурации и их исправление
Большинство проблем маршрутизации сводится к трём классам ошибок: неправильный порядок location-блоков, неверный proxy_pass, конфликтующие rewrite-правила. Разберём каждый с симптомами и решением.
Неправильный порядок location-блоков
Симптом: запросы к /api/ обрабатываются корневым location и возвращают 404 или HTML вместо JSON. Причина: префиксный location / объявлен раньше и перехватывает запросы, если более специфичный блок не имеет модификатора ^~ или не является регулярным выражением.
Решение: используйте ^~ для префиксных блоков, которые должны остановить дальнейший поиск:
location ^~ /api/ {
proxy_pass http://backend;
}
location / {
root /var/www/html;
}Модификатор ^~ говорит Nginx: если префикс совпал, не проверять регулярные выражения. Это устраняет неоднозначность.
Ошибки в proxy_pass: завершающий слэш и URI
Симптом: бэкенд получает неправильный путь, часть URI теряется или дублируется. Причина: разница между proxy_pass http://backend; и proxy_pass http://backend/;.
Без завершающего слэша Nginx передаёт URI целиком. С завершающим слэшем - заменяет совпавшую часть location на путь из proxy_pass:
location /api/ {
# Запрос /api/users -> бэкенд получит /api/users
proxy_pass http://backend;
}
location /api/ {
# Запрос /api/users -> бэкенд получит /users
proxy_pass http://backend/;
}Проверяйте фактический путь на бэкенде через его access-лог или добавьте на бэкенд временный вывод request_uri.
Проблемы с rewrite и редиректами
Симптом: бесконечный цикл редиректов или потеря query-параметров. Причина: rewrite-правило срабатывает повторно на уже изменённом URI, либо флаг last заставляет Nginx заново проходить весь цикл выбора location.
Решение: используйте флаг break, чтобы остановить дальнейшую обработку rewrite-модуля в текущем location:
location /old/ {
rewrite ^/old/(.*)$ /new/$1 break;
proxy_pass http://backend;
}Флаг permanent или redirect используйте для внешних редиректов, когда нужно изменить URL в браузере. Проверяйте цепочку через curl -L -v, чтобы увидеть каждый шаг и найти зацикливание.
Приоритеты location-блоков: как Nginx выбирает обработчик
Nginx выбирает location по строгому алгоритму. Сначала ищет точное совпадение с модификатором =. Затем проверяет префиксные блоки, запоминая самый длинный совпавший. Если среди них есть ^~, поиск останавливается. Иначе проверяются регулярные выражения в порядке объявления. Если регулярное выражение совпало, оно побеждает. Если нет - используется запомненный префиксный блок.
Модификаторы location: =, ^~, ~, ~*
| Модификатор | Тип | Приоритет | Когда использовать |
|---|---|---|---|
= | Точное совпадение | 1 (высший) | Для конкретных URI вроде /health |
^~ | Префиксный с остановкой | 2 | Для каталогов, которые не должны перехватываться регулярками |
~ | Регистрозависимое регулярное | 3 | Для шаблонов с учётом регистра |
~* | Регистронезависимое регулярное | 3 | Для шаблонов без учёта регистра |
| без модификатора | Префиксный | 4 (низший) | Для корневого location / |
Ошибка начинающих - ставить регулярное выражение и ожидать, что оно победит длинный префикс. На деле регулярка проверяется только если нет ^~ и нет точного совпадения. Порядок объявления регулярных выражений важен: первое совпавшее побеждает.
Практический пример: отладка выбора location
Конфигурация с несколькими блоками и маркерами:
location = /health {
echo 'MATCH: exact health';
}
location ^~ /static/ {
echo 'MATCH: static prefix';
}
location ~ \.(png|jpg)$ {
echo 'MATCH: image regex';
}
location / {
echo 'MATCH: root';
}Тесты:
curl http://example.com/health
# MATCH: exact health
curl http://example.com/static/logo.png
# MATCH: static prefix (^~ остановил поиск до регулярки)
curl http://example.com/photo.png
# MATCH: image regex
curl http://example.com/other
# MATCH: rootЭтот метод снимает все сомнения о фактическом выборе. Маркеры удаляются после завершения отладки.
Чек-лист: пошаговый алгоритм отладки маршрутизации
- Проверьте синтаксис конфигурации:
nginx -t. Ошибки на этом этапе блокируют запуск и не требуют дальнейшей диагностики. - Включите детальный log_format с
$upstream_addr,$request_uri,$request_timeи перезагрузите Nginx. - Добавьте
echo-маркеры во все location-блоки, участвующие в маршрутизации. - Прогоните проблемный запрос через
curl -Iиcurl -v. Зафиксируйте код ответа и заголовки. - Если есть редирект, проследите цепочку через
curl -L -w '%{url_effective}'. - Запустите
ngxtopс фильтром по статусу, чтобы увидеть масштаб проблемы. - Проанализируйте error.log на уровне
debugдля проблемного location. - Проверьте proxy_pass: наличие завершающего слэша, корректность upstream-адреса.
- Проверьте rewrite-правила на повторное срабатывание и циклы.
- Удалите отладочные маркеры и верните стандартный формат логов.
Для контроля состояния Nginx после исправлений настройте мониторинг ключевых метрик по шпаргалке из пяти метрик.
Заключение: системный подход экономит время
Отладка маршрутизации Nginx сводится к последовательности: логируй, помечай, тестируй, мониторь. Детальный access_log показывает фактический путь запроса. Echo-маркеры снимают вопрос о выборе location. curl проверяет заголовки и редиректы. ngxtop даёт общую картину. Разбор типичных ошибок в proxy_pass и rewrite закрывает оставшиеся случаи.
Применяйте этот алгоритм при каждом сбое маршрутизации. Время диагностики сокращается с часов до минут, а конфигурация становится предсказуемой. Перед обновлением Nginx или внесением изменений в продакшен используйте чек-лист резервного копирования, чтобы иметь возможность быстрого отката.