Как nginx выбирает location
- Сначала URI нормализуется: percent-декодирование, слияние повторяющихся слешей, разрешение
.и... Строка запроса никогда не участвует в сопоставлении location. - Сравниваются все префиксные location (без модификатора,
=,^~). Точное совпадение=побеждает сразу. Иначе запоминается самый длинный совпавший префикс. - Если у этого самого длинного префикса стоит
^~, он побеждает, и regex пропускаются. - Regex-location (
~с учётом регистра,~*без учёта) проверяются в порядке их появления в файле. Первое совпадение побеждает, даже если ранее совпал более длинный префикс. - Если ни один regex не совпал, используется запомненный самый длинный префикс.
- Внутри победившего 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", если нужно увидеть, какой блок ответил.