Quase toda configuração confusa de NGINX remonta a uma suposição: a de que os blocos são tentados na ordem em que você os escreveu. Não são, e a distância entre essa suposição e o algoritmo real é onde as tardes vão embora.
A ordem que de fato roda
- Correspondência exata. Um
location = /caminhoque casa encerra a busca de imediato. Nada mais é considerado. - A rodada de prefixos. Cada location de prefixo é verificado e o mais longo que casa é memorizado. Não o primeiro do arquivo, o mais longo.
- A saída antecipada. Se esse prefixo mais longo tiver
^~, ele vence agora e as expressões regulares nunca são tentadas. - Expressões regulares, tentadas na ordem em que aparecem no arquivo, e a primeira que casa vence, batendo o prefixo memorizado.
- Retorno. Se nenhuma expressão regular casou, o prefixo mais longo memorizado vence afinal.
A ordem do arquivo decide exatamente um desses cinco passos. Todo o resto é especificidade e modificadores.
A consequência que pega as pessoas
Considere uma configuração com um bloco de prefixo /images/ e, mais abaixo, location ~ \.(gif|jpg|png)$.
Uma requisição para /images/logo.gif não usa o bloco /images/. A rodada de prefixos o memoriza, então o passo quatro roda as expressões regulares, a extensão casa, e a regex vence. O bloco de aparência mais específica, escrito primeiro, perde para um geral escrito depois.
É por isso que "meu bloco /images/ está sendo ignorado" quase sempre é uma regex de extensão logo abaixo dele. O bloco não está sendo ignorado; está sendo superado por projeto.
O que ^~ realmente significa
Ele é lido como "prioridade maior", e esse é o modelo mental errado. ^~ significa pare antes das expressões regulares. Não torna o prefixo mais forte; remove o passo quatro da busca.
O que faz dele a correção exata para o caso acima. Mude o bloco para location ^~ /images/ e a regex de extensão nunca roda para URIs sob /images/, continuando a funcionar em todo o resto.
O mais longo, não o primeiro
Dentro da rodada de prefixos, /a/b/c/ vence /a/ para uma requisição a /a/b/c/d independentemente de qual aparece primeiro. Ordenar blocos de prefixo por legibilidade é seguro; ordenar blocos de regex muda o comportamento.
O que conferir quando uma requisição cai no lugar errado
Existe uma correspondência exata? Ela teria encerrado a busca no passo um.
Existe uma regex que casa? Se sim, e nenhum ^~ disparou, ela venceu todos os prefixos. Olhe abaixo do bloco que você esperava, não acima.
O prefixo que você esperava é mesmo o mais longo que casa? Um mais longo em outro ponto do arquivo leva a rodada.
Há dois blocos idênticos? O NGINX rejeita de saída um location exato ou de prefixo duplicado; uma regex duplicada fica simplesmente inalcançável.
O que quem estuda precisa saber de cor
O NGINX tenta correspondências exatas primeiro e para se uma casar; depois memoriza o prefixo mais longo, não o primeiro; então, a menos que esse prefixo tenha ^~, tenta expressões regulares na ordem do arquivo e deixa a primeira que casar vencer o prefixo; e só retorna ao prefixo se nenhuma regex casou. A ordem do arquivo importa para expressões regulares e para mais nada. ^~ é uma saída antecipada, e não um aumento de prioridade, e é a correção quando uma regex insiste em roubar tráfego de um bloco de prefixo.