ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

Nginx: try_files, index и приоритет location — как избежать 404 и ловушек rewrite

Разбираем, как Nginx выбирает location и что реально проверяет try_files, когда срабатывает index и где чаще всего появляются 404. Поясню разницу root и alias и дам готовые конфиги для статики, PHP и SPA без опасных rewrite.
Nginx: try_files, index и приоритет location — как избежать 404 и ловушек rewrite

В Nginx три директивы — location, try_files и index — чаще всего становятся причиной «магических» 404 и неожиданных редиректов. Обычно проблема не в том, что «Nginx сломан», а в том, что мы неверно представляем порядок принятия решения: какой location выбран, какой путь считается файловым, и когда Nginx пытается подставить индексный файл.

Ниже — практический разбор: как устроен приоритет выбора location, что делает try_files, как работает index, где всплывают root/alias и почему rewrite часто усложняет отладку. В конце — рабочие шаблоны для статики, PHP и SPA-роутинга.

Как Nginx выбирает location: приоритет без мифов

Когда приходит запрос, Nginx сначала выбирает блок server (по listen и server_name), а затем внутри него подбирает один location. Это ключ к диагностике 404: выбранный location определяет и root/alias, и наличие try_files/index, и любые rewrite.

Краткий порядок выбора location

В реальных конфигах важнее всего помнить про тип совпадения:

  • location = /exact — точное совпадение, самый высокий приоритет.
  • location ^~ /prefix/ — префикс с запретом дальнейшего выбора regex-location (если префикс совпал).
  • location /prefix/ — обычный префикс: выбирается самый длинный совпавший префикс, но затем Nginx ещё может проверить regex.
  • location ~ regex и location ~* regex — регулярки (с учётом регистра и без). Если regex совпал, он выигрывает у обычного префикса.

Практическая ловушка: вы ожидаете, что запрос пойдёт в location / с try_files, а его перехватывает location ~ \.php$ или другая регулярка — и дальше логика уже совсем другая.

Как быстро понять, какой location сработал

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

  • посмотреть error_log (часто хватает уровня info);
  • проверить, нет ли regex-location, который «перехватывает» запрос;
  • временно упростить конфиг: отключить подозрительные regex-локации и проверить, исчезает ли 404.

Если вы видите 404 «на ровном месте», сначала докажите себе, что запрос реально попадает в тот location, в котором вы правите try_files и root/alias.

try_files: что именно проверяется и почему это главный анти-404

try_files — это последовательная проверка «существует ли файл/директория» с выбором первого удачного варианта. Если ни один вариант не найден — выполняется последний аргумент (обычно это URI на обработчик или явный код ошибки).

Ключевой момент: try_files проверяет файловую систему, используя текущие root/alias и значение URI. Поэтому любая ошибка в путях или в выборе location мгновенно превращается в 404.

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

Классический безопасный вариант:

location / {
    try_files $uri $uri/ =404;
}
  • $uri — пробуем отдать файл как есть.
  • $uri/ — пробуем директорию (полезно для «человеческих» URL и работы index).
  • =404 — если ничего не нашли, отдаём 404 явно, без неявных редиректов.

Типовая связка с index

index срабатывает тогда, когда запрос указывает на директорию (реальную или «разрешённую» через try_files $uri/). Тогда Nginx пытается найти один из индексных файлов.

server {
    root /var/www/site/public;
    index index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }
}

Если забыть $uri/, запрос /docs/ может не перейти в режим «директории», и index просто не будет применён.

Схема выбора location и прохождения запроса в Nginx

try_files для PHP: правильная идея и частая ошибка

Во многих PHP-приложениях нужен fallback на index.php (front controller). Важно: fallback держите в «корневом» location /, а обработчик PHP делайте отдельным location для реальных .php — иначе легко получить петли и неочевидные статусы.

server {
    root /var/www/site/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php-fpm.sock;
    }
}

Обратите внимание на fastcgi_param SCRIPT_FILENAME: ошибка в этой строке — один из самых частых источников ситуации «404, но файл же есть». На самом деле PHP-FPM получает путь не к тому файлу.

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Чем try_files лучше rewrite (и где начинаются ловушки)

rewrite меняет URI по правилам регулярных выражений. Это полезно, но часто становится источником проблем:

  • непредсказуемые цепочки переходов из-за флагов (last, break, redirect);
  • лишняя стоимость на большом числе regex;
  • сложнее отладка: URI переписали — и уже неясно, какой путь реально проверяется на диске.

Для типового сценария «если файл существует — отдай, иначе — в контроллер» почти всегда достаточно try_files, и это заметно прозрачнее.

index: как Nginx ищет индексный файл на самом деле

index — это список имён файлов, которые Nginx пытается открыть, когда URI указывает на директорию. По умолчанию часто встречается index index.html или index index.php index.html.

Три практические детали:

  • index применяется только для директории: реальной или «разрешённой» через try_files $uri/.
  • порядок важен: если первым стоит index.php, то даже при наличии index.html будет выбран PHP (если файл существует и PHP-локация его обработает).
  • листинг не включается сам: если индекса нет, вы получите 403/404 (по ситуации), но не список файлов, пока явно не включите autoindex on.

Если вы мигрируете сайт на новый сервер или меняете структуру путей, полезно держать под рукой чеклист про редиректы и HTTPS, чтобы не «потерять» страницы в процессе: 301-редиректы, HSTS и SSL при переносе домена.

root и alias: почему это вечный источник 404

root и alias выглядят похоже, но ведут себя по-разному:

  • root добавляет URI к пути: итоговый файл = root + URI.
  • alias заменяет совпавшую часть location на путь alias.

Когда нужен root

Если вы хотите, чтобы структура URL повторяла структуру каталогов под root, используйте root.

location /static/ {
    root /var/www/site/public;
    try_files $uri =404;
}

Запрос /static/app.js будет искать файл /var/www/site/public/static/app.js.

Когда нужен alias (и где чаще ошибаются)

alias удобен, когда URL не должен включать часть пути на диске.

location /static/ {
    alias /var/www/site/assets/;
    try_files $uri =404;
}

Теперь запрос /static/app.js ищет /var/www/site/assets/app.js.

Ловушка №1: слеши. Для location, оканчивающегося на /, alias почти всегда тоже должен оканчиваться на /, иначе пути будут склеиваться неожиданно.

Ловушка №2: регулярки и переписывание URI. Конструкции с alias хуже переносят усложнение логики, и в спорных случаях проще нормализовать структуру каталогов и вернуться к root.

SPA routing: как отдавать index.html и не ломать статику

Для SPA (React/Vue/Angular) маршрутизация живёт на клиенте, а сервер должен:

  • отдавать реальные файлы (JS/CSS/картинки) как есть;
  • на все «нефайловые» URL отдавать /index.html.

Самый простой и безопасный вариант — fallback на /index.html через try_files:

server {
    root /var/www/spa/dist;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

Если пользователь открывает /account/settings, файла такого нет — Nginx отдаёт index.html, а роутер SPA уже внутри браузера отображает нужную страницу.

Отдельно: 404 для отсутствующих ассетов

Иногда нужно, чтобы «битые» ассеты не маскировались под 200 с index.html (иначе браузер может закешировать HTML вместо JS). Для этого обычно выносят ассеты в отдельный префикс:

location /assets/ {
    try_files $uri =404;
    expires 30d;
}

А общий location / оставляют с fallback на /index.html.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Типовые 404 в Nginx: чеклист диагностики

Когда вы ловите 404, удобно проверять по порядку:

  1. Тот ли server? Совпадает ли server_name, нет ли default_server, который перехватывает домен.
  2. Тот ли location? Нет ли regex, который выигрывает у префикса. Помните про ^~ и =.
  3. Правильный ли файловый путь? Сверьте root/alias и реальное расположение файла на диске.
  4. Не ломает ли fallback? Частая ошибка — fallback на URI, который снова попадает в тот же location и не находит файл.
  5. Для PHP: проверьте SCRIPT_FILENAME, fastcgi_pass, наличие файла и права.

Готовые шаблоны: что ставить «по умолчанию»

Статический сайт с красивыми URL и явным 404

server {
    root /var/www/site/public;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

Пример конфигурации Nginx с try_files и обработкой PHP через PHP-FPM

PHP-приложение с фронт-контроллером (index.php)

server {
    root /var/www/app/public;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php-fpm.sock;
    }

    location ~ /\. {
        return 404;
    }
}

SPA с отдельным 404 для ассетов

server {
    root /var/www/spa/dist;
    index index.html;

    location /assets/ {
        try_files $uri =404;
        expires 30d;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}

Итоги: как перестать «лечить 404» переписыванием

Если держать в голове три опоры — выбор location, корректный root/alias и понятный try_files — большинство проблем исчезает без сложных rewrite.

Минимальный практический совет: сначала сделайте «прозрачный» конфиг, который отдаёт файлы и директории через try_files, и только потом добавляйте regex-location и rewrite. Так вы быстро локализуете, где именно появляется расхождение между URI и реальным путём на диске.

Если вы размещаете несколько проектов и хотите меньше возиться с конфликтами портов, разными версиями PHP и отдельными Nginx-конфигами, часто удобнее выделить проекту отдельную VDS или использовать виртуальный хостинг под более простой стек.

Поделиться статьей

Вам будет интересно

Let’s Encrypt wildcard через DNS-01: acme.sh, TSIG для BIND9 и сценарии с NSD без ручных правок OpenAI Статья написана AI (GPT 5)

Let’s Encrypt wildcard через DNS-01: acme.sh, TSIG для BIND9 и сценарии с NSD без ручных правок

Практическая настройка DNS-01 для wildcard-сертификатов Let’s Encrypt с acme.sh: создание TSIG-ключей, update-policy в BIND9, пров ...
SSH hardening на VDS: Fail2ban vs sshguard, Match blocks и безопасные политики доступа OpenAI Статья написана AI (GPT 5)

SSH hardening на VDS: Fail2ban vs sshguard, Match blocks и безопасные политики доступа

Пошагово укрепляем SSH на VDS без лишней паранойи: готовим аварийный откат, переводим вход на ключи, запрещаем root и пароли, огра ...
PostgreSQL: auto_explain, pg_stat_statements и простая APM-диагностика запросов OpenAI Статья написана AI (GPT 5)

PostgreSQL: auto_explain, pg_stat_statements и простая APM-диагностика запросов

Пошагово настраиваем pg_stat_statements и auto_explain для поиска медленных запросов без тяжёлых APM-систем. Разберём log_min_dura ...