Tendo esta configuração nginx simples:
location / {
return 200 "Location 1\n";
}
location ~ \.php$ {
return 200 "Location 2\n";
}
location /tmp {
return 200 "Location 3\n";
location ~ \.php$ {
return 200 "Location 3a\n";
}
}
Por que as /tmp/foo.php
solicitações fornecem a Location 3a
resposta, embora, de acordo com a documentação , o local da regex Location 2
deva ultrapassar a solicitação, a menos que o local do prefixo Location 3
tenha o ^~
modificador?
Isso acontece porque o algoritmo real para selecionar um local no nginx difere do que está descrito na documentação. Ou, para ser mais específico, a documentação oficial não explica o processo de seleção de local para locais aninhados, o que é consideravelmente mais complexo. Até agora, não encontrei nenhum artigo em inglês explicando como ele realmente funciona, então aqui está minha tentativa de esclarecê-lo.
Vamos começar com a configuração fornecida na pergunta original:
Pode-se pensar que a solicitação
/tmp/foo.php
será processada comlocation ~ \.php$ { ... }
(marcado como "Local 2"), pois a documentação diz explicitamente:Vamos verificar:
Inesperado, não é? O que a documentação não diz é que, após identificar o local de prefixo correspondente mais longo, o nginx inicia uma nova iteração de busca sobre seus locais aninhados, se houver. Esse processo continua recursivamente: em cada etapa, as mesmas regras de correspondência são aplicadas e, se um local de regex aninhado correspondente for encontrado, enquanto nenhum local de prefixo mais longo aninhado for correspondido, o nginx interrompe a busca e usa esse local para manipular a solicitação.
No nosso exemplo, para a
/tmp/foo.php
solicitação:location /tmp { ... }
(marcado como "Local 3").location ~ \.php$ { ... }
(marcado como "Localização 3a") corresponde e é usado para manipular a solicitação.Agora, vamos estender a configuração adicionando um quarto local:
Testando a mesma solicitação:
O que aconteceu aqui?
location /tmp/foo { ... }
.location ~ \.php$ { ... }
corresponde e é usado para manipular a solicitação.Outras solicitações como essas
/tmp/bar.php
continuam sendo processadas como antes:Em seguida, vamos modificar a configuração novamente, movendo o
location /tmp/foo { ... }
para dentro dolocation /tmp { ... }
:Executando alguns testes:
Nada inesperado até agora.
Agora, vamos examinar o
^~
modificador de diretiva location. De acordo com a documentação:Vamos verificar usando a seguinte configuração:
Executando alguns testes:
Parece que tudo funciona como esperado. O
^~
modificador onlocation ^~ /tmp/foo { ... }
garante que os mesmos locais de regex de nível, comolocation ~ \.php$ { ... }
, não possam ultrapassar solicitações correspondentes como/tmp/foo.php
.Agora, aqui está algo que pode realmente surpreender você. Vamos modificar nossa configuração, movendo o
location ^~ /tmp/foo { ... }
para dentrolocation /tmp { ... }
novamente:Vamos testar a mesma
/tmp/foo.php
solicitação. Você está esperando que a resposta sejaLocation 3b
, certo?Oops... Parece estranho, não é? Bem, esse é provavelmente o último aspecto do algoritmo que precisa ser explicado. Após identificar o local de prefixo correspondente mais longo no nível mais aninhado (no nosso caso, o
location ^~ /tmp/foo { ... }
, marcado como "Localização 3b"), se nenhum local de regex correspondente for encontrado dentro dele, o nginx lembra desse local e começa a subir a árvore de configuração de volta ao nível mais externo. Em cada nível que ele passa durante a subida, as seguintes ações são tomadas:^~
modificador, o nginx corresponde à solicitação em relação a todos os locais de regex definidos naquele nível. O primeiro local de regex correspondente, se houver, é usado para manipular a solicitação; caso contrário, o nginx continua ascendente.^~
modificador, o nginx pula a correspondência da solicitação com os locais de regex definidos naquele nível e imediatamente sobe para o próximo nível acima. Se ele já tiver atingido o nível mais externo, o local de prefixo correspondente mais longo lembrado do nível mais aninhado é usado para manipular a solicitação.É isso. Ter o
^~
modificador em um local de prefixo aninhado (localização 3b) não garante que a solicitação não será ultrapassada por um local de regex externo (localização 2) se o local de prefixo correspondente mais longo externo (localização 3) não tiver^~
modificador.PS Claro, deve ser mencionado que se um local de correspondência exata (um local com o
=
modificador) for encontrado em qualquer nível durante a descida, o nginx para imediatamente de procurar, e o local encontrado é usado para manipular a solicitação. Considerando isso, uma configuração típica do nginx comopode ser significativamente otimizado se o
index.php
arquivo for o único manipulador das solicitações, o que é comum em muitos casos de uso: