После Nginx исчез префикс URL: как завершающий слеш меняет proxy_pass

Приложение работает на /api/users, а после подключения Nginx отвечает 404. Прокси до backend доходит, порт открыт, health работает. Ошибка может быть в преобразовании URI: завершающий слеш в proxy_pass задаёт не оформление строки, а URI, которым заменяется совпавшая часть пути.

Сначала узнайте, какой путь нужен backend

Предположим, внешний клиент вызывает /api/users. Если backend ожидает /users, префикс нужно убрать. Если он сам обслуживает /api/users, путь нужно сохранить. Это решение следует из маршрутов приложения, а не из общего правила «всегда ставить слеш».

location /api/ {
    proxy_pass http://127.0.0.1:8080/;
}

В обычном prefix location приведённая настройка заменяет совпавший нормализованный префикс /api/ на URI / из proxy_pass. Поэтому запрос /api/users попадёт как /users. Для сервиса с маршрутами без внешнего префикса это нужное поведение.

location /api/ {
    proxy_pass http://127.0.0.1:8080;
}

Вторая настройка не содержит URI после адреса upstream. В простом случае без переписывания пути исходный /api/users передаётся дальше. Нельзя оставить обе location одновременно: это два альтернативных варианта. Выберите один под контракт приложения.

Проверьте реальный запрос

curl -i http://127.0.0.1:8080/api/users
curl -i http://127.0.0.1:8080/users
curl -i https://app.example/api/users

Первые две команды запускаются там, откуда backend действительно доступен, и показывают, какой маршрут он понимает. Третья проверяет внешний путь. Используйте read-only эндпоинт тестового стенда и сопоставьте запрос с журналом приложения. Ответ 404 от Nginx и 404 от backend полезно различать именно по журналу, а не по оформлению страницы.

Дополнительно проверьте /api без завершающего слеша, вложенный путь и query string. У location со слешем есть особое поведение перенаправления для запроса без него. Если внешний клиент чувствителен к redirect, явно определите, какой URL вы считаете каноническим, и проверьте соответствующий ответ.

Не расширяйте правило за пределы примера

Regex location, named location, rewrite с изменением URI и переменные в proxy_pass меняют условия обработки. Для них нельзя механически переносить объяснение двух простых блоков. Если конфигурация уже использует такие конструкции, сначала посмотрите полный маршрут обработки запроса и упростите воспроизведение на стенде.

Перед применением выполните nginx -t для согласованного конфигурационного набора. Он проверяет синтаксис и доступность связанных файлов, но не знает маршрутов приложения. Поэтому после применения требуется именно HTTP-проверка и подтверждение URI у backend.

Запишите соответствие в виде пары: внешний /api/users становится внутренним /users. Такая короткая запись предотвращает повторную ошибку при переносе блока в другую среду. Если приложение строит абсолютные ссылки, отдельно согласуйте его внешний префикс: входящий rewrite не переписывает автоматически всё содержимое ответа.

Продолжите от одного прокси-правила к диагностике сервиса. Откройте программу Linux и Bash для DevOps-инженеров. Работа с сетью, HTTP и учебным приложением поможет проверять не только доступность backend, но и смысл передаваемого запроса.

Ещё по теме

Продолжить на практике

  • Linux и Bash для DevOps-инженеров ↗

    Linux для работы с серверами: процессы, права, systemd, сеть, диски и логи. Автоматизируйте повседневные задачи на Bash и разберите типичные сбои сервиса — от занятого порта до переполненного диска.