- Нормализация происходит до обращения к любому location
- URI раскодируется из процентных последовательностей, сегменты
. и .. разрешаются, повторяющиеся слеши схлопываются — и только затем выбирается location. Раскодированный %2F становится настоящим разделителем и участвует в этом разрешении, поэтому /a/b%2F..%2Fzz сопоставляется как /a/zz. Три раскодированных символа — исключение и остаются буквальными: %25, %23 и %3F, из-за чего у /a%3Fx=1 в пути стоит знак вопроса, а строка запроса пуста. Знак + здесь не пробел; пробелом является только %20. Выход выше корня и некорректное экранирование отклоняются с кодом 400 ещё до начала сопоставления.
- Решает длина префикса, а не порядок в конфигурации
- Сравниваются все совпавшие префиксные location, и побеждает самый длинный — независимо от того, стоит он в файле первым или последним. Сравнение идёт по символам, а не по сегментам пути, поэтому
/static совпадает с /staticfoo. Победитель запоминается, а не используется сразу, потому что этап регулярных выражений ещё может его перебить.
- ^~ подавляет регулярные выражения, а не повышает приоритет
- Модификатор проверяется только у того префикса, который уже победил по длине. Если совпадает и более длинный обычный префикс, запоминается именно он, а этап регулярных выражений идёт как обычно — блок
^~ не влияет на запрос вообще. ^~ на вложенном location не защищает от регулярного выражения, объявленного на внешнем уровне, и никогда не подавляет регулярные выражения, вложенные внутрь самого блока.
- Регулярные выражения идут в порядке файла, и первое совпадение завершает поиск
- nginx хранит регулярные location в том порядке, в каком они были написаны, и не сортирует их. Точность, длина и якоря никак не влияют на то, какое будет проверено первым, поэтому точный шаблон, поставленный ниже широкого, — мёртвая конфигурация. Внутрь совпавшего регулярного location тоже происходит спуск, поэтому вложенные в него location просматриваются следом.
- PCRE и ECMAScript — не один и тот же язык
- nginx компилирует регулярные выражения location через PCRE, без режимов UTF и multiline, поэтому шаблоны работают с байтами, а
^ привязывается только к началу URI. Одно различие имеет вес для безопасности: $ в PCRE совпадает и прямо перед завершающим переводом строки, поэтому URI, оканчивающийся на %0A, всё равно удовлетворяет \.php$, хотя движок JavaScript отказал бы. Это поведение здесь эмулируется и отмечается, потому что это известный способ проскочить мимо правил, завязанных на расширение файла.