bitlvl
RU

Тестер location nginx

nginx выбирает location по правилам, которые легко запомнить неправильно, а обычный способ проверить — задеплоить и дёрнуть curl. Вместо этого вставьте конфиг и URL: трассировка показывает каждый рассмотренный location, почему он выиграл или проиграл, и что происходит дальше.

Ничего никуда не отправляется; сопоставление выполняется в вашем браузере.

Результаты и сообщения этого инструмента выводятся на английском языке.

Как nginx выбирает location

  1. Сначала URI нормализуется: percent-декодирование, слияние повторяющихся слешей, разрешение . и ... Строка запроса никогда не участвует в сопоставлении location.
  2. Сравниваются все префиксные location (без модификатора, =, ^~). Точное совпадение = побеждает сразу. Иначе запоминается самый длинный совпавший префикс.
  3. Если у этого самого длинного префикса стоит ^~, он побеждает, и regex пропускаются.
  4. Regex-location (~ с учётом регистра, ~* без учёта) проверяются в порядке их появления в файле. Первое совпадение побеждает, даже если ранее совпал более длинный префикс.
  5. Если ни один regex не совпал, используется запомненный самый длинный префикс.
  6. Внутри победившего location та же процедура выполняется заново для вложенных location.

Два следствия удивляют чаще всего: простой location /images/ проигрывает location ~ \.png$ для /images/a.png, потому что regex проверяются после префиксов; а location /api совпадает и с /apiary, потому что префиксы — это префиксы строки, а не сегменты пути. Используйте /api/ или ^~, когда имеете в виду именно это.

proxy_pass и слеш в конце

Если у proxy_pass есть URI-часть (что угодно после хоста, даже просто /), nginx заменяет ею ту часть URI запроса, которая совпала с префиксом location. location /api/ { proxy_pass http://app/; } превращает /api/users в /users. Без URI-части URI запроса передаётся без изменений. Несогласованные слеши (/api против /api/ или http://app против http://app/) дают //users или users — тестер выводит точный URI для upstream, чтобы вы это увидели. URI-часть не допускается в regex-location, если только rewrite не изменил URI заранее.

root против alias

root добавляет весь URI к каталогу: root /srv; location /static/ отдаёт /srv/static/x.css. alias заменяет префикс location: alias /srv/assets/; location /static/ отдаёт /srv/assets/x.css. С alias слеши в конце location и alias должны совпадать, а в regex-location alias должен использовать группы захвата.

Флаги rewrite

ФлагЭффект
lastпрекратить обработку rewrite и заново начать сопоставление location с новым URI (не более 10 раз, затем 500)
breakпрекратить rewrite и остаться в текущем location с новым URI
redirectотправить клиенту 302
permanentотправить клиенту 301
(нет)продолжить со следующим rewrite; на уровне server результат затем сопоставляется с location

try_files

Каждый аргумент проверяется как файл (или каталог, со слешем в конце) относительно root/alias; первый существующий отдаётся. Последний аргумент — запасной вариант, и он не проверяется: это внутренний редирект на URI (который снова проходит сопоставление location), именованный location @name или код состояния вида =404. Тестер не видит вашу файловую систему, поэтому перечисляет пути, которые были бы проверены.

Наследование add_header

add_header наследуется с уровня server только тогда, когда у location нет собственного add_header. Один add_header Cache-Control в location молча отбрасывает заголовки безопасности, заданные на уровне server. Трассировка это отмечает.

Вопросы

Поддерживается ли полный синтаксис конфигурации nginx?

Парсер обрабатывает реальный синтаксис: вложенные блоки, комментарии, строки в кавычках, regex-аргументы; директивы include перечисляются, но не раскрываются (вставьте и включаемый файл). Переменные, кроме $uri, $request_uri, $args и групп захвата regex, остаются текстом, а блоки «if» показываются, но не вычисляются.

Почему мой regex ведёт себя в nginx иначе?

nginx использует PCRE; эта страница — регулярные выражения JavaScript. Они согласуются почти во всём, что используется в блоках location, но притяжательные квантификаторы, рекурсия и некоторые формы lookbehind различаются. Трассировка сообщает, если regex не удалось скомпилировать.

Можно ли проверить, какой блок server отвечает для Host?

Да. Вставьте несколько блоков server и заполните поле Host. Выбор следует логике nginx: точное имя, самый длинный wildcard, начинающийся с *, самый длинный wildcard, заканчивающийся на *, первый совпавший regex, затем default_server, затем первый server для этого порта.

Как проверить на реальном сервере?

nginx -T выводит полную объединённую конфигурацию (все include раскрыты), которую можно вставить сюда. Для живой проверки выполните curl -sI -H "Host: example.com" http://127.0.0.1/path и прочитайте ответ; временно добавьте add_header X-Debug-Location "$uri", если нужно увидеть, какой блок ответил.