Introdução
Nginx está entre as opções de servidor web mais populares do mundo. Ele é capaz de lidar com sucesso com uma infinidade de conexões simultâneas de clientes. Ao mesmo tempo, funciona como um servidor de e-mail, web ou proxy reverso.
Este guia tem como objetivo delinear os métodos nos bastidores que direcionam como o Nginx processa as solicitações dos clientes. Vamos desmistificar o design de blocos server e location, bem como explicar a melhor forma de reduzir a aparente imprevisibilidade do tratamento de solicitações.
Primeiro as coisas mais importantes, aqui está um tutorial abrangente sobre como instalar o Nginx no seu servidor Ubuntu. Agora, vamos começar!
Configuração de Blocos com o Nginx
A abordagem lógica do Nginx envolve a ordenação das configurações destinadas a diferentes propósitos em blocos de conteúdo separados e mais lógicos. Estes residirão em uma estrutura hierárquica. Quando um cliente faz uma solicitação, o Nginx inicia um processo pelo qual determina quais desses blocos de configuração são os mais aplicáveis para atender a essa solicitação. Focaremos neste processo de decisão.
Os blocos primários que discutiremos serão os blocos server e os blocos location. Os blocos server são um subconjunto das configurações que o Nginx estabelece que definem qual servidor virtual será responsável por lidar com um tipo definido de solicitação. Eles são mais comumente baseados no endereço IP, nome de domínio ou porta da solicitação recebida. Os administradores configuram múltiplos blocos server. Depois, eles precisam decidir qual das conexões deve lidar com a solicitação.
Os blocos location residem dentro dos blocos server. Estes são os tomadores de decisão sobre como e quais recursos você pode aproveitar para lidar com as solicitações recebidas para seu servidor pai específico. Este modelo é altamente flexível. O espaço URI pode ser configurado para usar esses blocos da maneira que o administrador achar melhor.
Decidir Qual Bloco Tratará Cada Solicitação com o Nginx
O Nginx permite a definição de múltiplos blocos server. Todos eles funcionam como diferentes servidores web virtuais. Portanto, é necessário um método que delineie qual servidor responderá a solicitações recebidas específicas. Isso é feito encontrando a melhor adequação para o desempenho da solicitação por meio de um sistema de verificações definidas.
O Nginx lida principalmente com duas diretivas principais de bloco server: listen e server_name.
Encontrar Correspondências Possíveis com a Diretiva 'Listen'
A primeira coisa que o Nginx avalia é a porta e o endereço IP da solicitação. Em seguida, ele faz a correspondência com a diretiva listen de cada servidor. Essa análise da lista de servidores ajuda a isolar apenas os blocos server que podem resolver a solicitação em questão.
Normalmente, a diretiva listen definirá a porta e o endereço IP pelos quais um bloco server específico será responsável por responder. Um bloco server que não apresenta uma diretiva listen recebe os parâmetros de escuta de 0.0.0.0:80 por padrão. Se o Nginx for executado por um usuário normal, não root, o parâmetro listen é definido como 0.0.0.0:8080. Isso significa que, independentemente da interface, se os blocos estiverem vindo da porta 80, os blocos definidos dessa maneira responderão a eles. No entanto, esse valor padrão não tem grande peso no processo de seleção de um servidor.
Você pode configurar a diretiva listen para:
- Um endereço IP único que escuta solicitações na porta padrão (80).
- Uma porta única que escuta qualquer interface nessa porta.
- Uma combinação de porta e endereço IP.
- Um caminho de socket Unix definido (esta opção só tem implicações quando as solicitações passam por servidores diferentes).
O Nginx implementará um conjunto de regras ao decidir para qual bloco server uma solicitação será enviada. As regras dependem da configuração específica da diretiva listen. Elas são as seguintes:
- Se uma diretiva listen estiver incompleta, as partes ausentes recebem seus valores padrão. Isso significa que o endereço IP e a porta serão forçados a serem preenchidos com valores padrão para processar a solicitação.
- Neste caso, um bloco que não contém nenhuma diretiva listen usará o valor padrão de 0.0.0.0:80.
- Um bloco que não possui uma porta e tem um endereço IP de 111.111.111.111 se tornará 111.111.111.111:80.
- Quando não houver endereço IP, um bloco com a porta 8888 adquirirá o endereço IP padrão para anexar e criar 0.0.0.0:8888.
- Tendo o endereço IP e a porta determinados, o Nginx procurará por blocos de servidor oferecidos como correspondência para essa porta.
- Se encontrar apenas uma correspondência específica, este será o bloco de servidor. Se houver múltiplos blocos qualificados, o Nginx recorrerá à diretiva server_name para detalhar ainda mais até o bloco de servidor exato em questão.
O Nginx só recorrerá à avaliação da diretiva server_name se não encontrar o bloco de servidor com o nível exato de especificidade da diretiva listen. Se o example.com estiver na porta 80, com um IP de 192.168.1.10, o primeiro bloco neste exemplo será sempre o que acomodará a requisição. Este é o caso independentemente do que diz a diretiva server_name:

Se houver mais de um bloco de serviço qualificado com uma correspondência de especificidade, então a diretiva server_name será levada em consideração.
Encontrar Correspondências Possíveis com a Diretiva ‘Server_Name’
Se as diretivas listen forem igualmente específicas, o Nginx verificará o cabeçalho ‘Host’ da requisição’. Este é um valor que terá o IP do domínio que o cliente estava tentando alcançar, inicialmente. O Nginx utilizará a diretiva server_name dentro de cada candidato a bloco de servidor ainda qualificado. Ele realiza essas avaliações com base em uma fórmula. Ela é a seguinte:
- A primeira tentativa do Nginx será identificar um bloco com um server_name que corresponda exatamente ao valor do cabeçalho ‘Host’ na requisição. Se o encontrar, o bloco que contém a correspondência exata será o responsável por atender à requisição. Caso encontre múltiplos blocos, ele escolherá o primeiro da lista.
- Se não houver correspondências exatas, o Nginx tentará usar o server_name para encontrar o bloco de servidor que corresponda com o uso de *, um caractere curinga no início do nome do bloco de servidor na configuração. Encontrar um por este método significa que o bloco de servidor foi determinado. Se encontrar mais de uma correspondência, a correspondência mais longa será a que atenderá à requisição.
- Sem um curinga correspondente, o Nginx tentará encontrar um bloco de servidor com um curinga no final correspondente. Em outras palavras, este será um nome de servidor com um * na configuração. Se um for encontrado, ele será usado para a requisição. Enquanto que, se encontrar múltiplos, o Nginx usará novamente a correspondência mais longa.
- Caso ainda não haja correspondências após ambas as tentativas com curingas, o Nginx avaliará os blocos de servidor que definem o server_name usando expressões típicas (designadas por um ~ antes do nome). A primeira instância de server_name com uma expressão que corresponda à do cabeçalho ‘Host’ será considerada como o bloco de servidor para o tratamento da requisição.
- Se neste ponto ainda não houver correspondências, o Nginx usará o bloco de servidor padrão para essa combinação de porta e endereço IP.
Cada combinação de porta/endereço IP terá um bloco de servidor designado. Ele será usado se as regras para determinar o bloco de servidor apropriado para o tratamento da requisição forem infrutíferas. Este será o primeiro bloco na configuração que contém uma opção default_server na diretiva listen (ele substituiria o algoritmo inicialmente encontrado). Cada combinação de endereço IP/porta só pode ter, no máximo, uma configuração default_server.
Exemplos de Seleção de Bloco de Servidor
Se o server_name definido corresponder exatamente ao valor do cabeçalho ‘Host’, ele será o bloco de servidor selecionado para o processamento da requisição. O exemplo a seguir mostra um cabeçalho ‘Host’ da requisição designado como “host1.example.com”. Neste caso, ele selecionará o segundo servidor:

Sem uma correspondência exata, o Nginx verificará se o server_name com um curinga existe. Caso contrário, a correspondência mais longa começando com um curinga será selecionada. No seguinte, “www.example.org” está no cabeçalho “Host”. Isso significa que ele escolherá o segundo bloco:

Sem uma correspondência que comece com o caractere curinga, o Nginx passa para uma verificação de caractere curinga final. A correspondência mais longa que termina com o caractere curinga será selecionada para o processamento da requisição. Neste caso, o cabeçalho “Host” é “www.example.com”, por isso escolherá o terceiro bloco de servidor:

Se ainda assim não houver correspondências, o Nginx tentará corresponder as diretivas server_name usando expressões padrão. A primeira dessas expressões é selecionada para o processamento da requisição. Se o “Host” for “www.example.com,” o segundo bloco de servidor será a escolha para atender à requisição:

Se ainda assim não houver correspondências, a requisição irá para a combinação de endereço IP e porta com a configuração de servidor padrão correspondente.
Correspondência de Bloco Location
O Nginx também precisa estabelecer um algoritmo pelo qual decidirá qual bloco location no servidor será responsável por responder a uma requisição.
Sintaxe para Blocos Location
Antes de explicar como o Nginx decide como designar o bloco location que processará as requisições, revisaremos a sintaxe nas definições de blocos location. Como mencionado anteriormente, os blocos location residem em blocos server (e outros blocos location). O objetivo deles é tomar decisões sobre como processar a URI da requisição. A URI é a parte da requisição que vem após o endereço IP e a porta ou o nome de domínio na requisição.
Os blocos location normalmente se parecem com isto:

O Nginx verificará a URI da requisição em relação ao location_match. A presença ou não do modificador acima ditará a maneira pela qual o Nginx tentará corresponder os blocos. Dependendo do modificador, os blocos location serão interpretados de acordo com as seguintes regras:
- Sem modificadores: Sem modificadores, o location será interpretado como uma correspondência de prefixo. Isso significa que o location fornecido será comparado com o início da URI na requisição para determinar uma correspondência correta.
- =: O sinal de igual significa que este bloco será considerado uma correspondência, desde que a URI da requisição corresponda exatamente ao location fornecido.
- ~: O modificador til representa que a correspondência do bloco location será sensível a maiúsculas e minúsculas.
- ~*: A combinação de um modificador til e um asterisco representa que o bloco location será insensível a maiúsculas e minúsculas ao buscar uma correspondência.
- ^~: Se o modificador til for precedido por um acento circunflexo, a correspondência de expressão regular não ocorrerá, desde que este bloco seja escolhido como a melhor correspondência de expressão não regular.
Exemplos de Sintaxe de Bloco Location
Para apresentar um exemplo de correspondência de prefixo, o bloco location será a seleção para responder a uma URI de uma requisição na forma de /site, /site/page1/index.html ou /site/index/html:

Para fins desta demonstração de correspondência de URI necessária, o bloco sempre será usado para responder a requisições de URI na forma de /page1, e não à URI de requisição /page1/index.html. Se este for o bloco selecionado e ele atender à requisição usando uma página de índice, o manipulador real da requisição será redirecionado internamente para outro location:

Por exemplo, um location que deve ser interpretado com uma expressão sensível a maiúsculas e minúsculas, o seguinte bloco não poderia lidar com requisições para /FLOWER.PNG. No entanto, ele lidará com requisições para /tortoise.jpg:

A seguir, observe um bloco que permitiria a correspondência insensível a maiúsculas e minúsculas semelhante ao anterior. Neste caso, o bloco poderia lidar tanto com //tortoise.jpg e /FLOWER.PNG:

A variante final é aquela em que um bloco impediria a ocorrência de correspondência de expressão regular se for determinado que ele é a correspondência ideal de expressão não regular. Este pode lidar com requisições para /costumes/ninja.html:

Para ser mais preciso, os modificadores ditam a maneira como os blocos location são determinados. Isso não nos diz, no entanto, o que o Nginx utiliza como algoritmo de tomada de decisão para identificar o bloco location para o qual uma requisição deve ser enviada. Vamos abordar isso a seguir.
Escolha o Location que Processará as Requisições pelo Nginx
O método pelo qual o Nginx escolhe o location que processa uma requisição é semelhante a como os blocos de servidor (server blocks) são selecionados. Em outras palavras, ele determina o location ideal para cada requisição executando um processo. Para configurar o Nginx de forma precisa e adequada, é imperativo que você entenda esse processo.
Tendo em mente as declarações de location abordadas anteriormente, o Nginx usa de forma semelhante contextos de location potenciais, verificando a qualificação de cada location ao comparar com ele a URI de uma determinada requisição. Nisso, ele aplica o seguinte algoritmo:
- Primeiro, o Nginx verifica todos os tipos de location que não incluem uma expressão regular. Ele faz isso buscando todas as correspondências de prefixo baseadas em location. Para fazer isso, ele verifica o location em relação à URI completa da requisição.
- O Nginx começa procurando por uma correspondência exata. Assim que um bloco location que usa o modificador = é identificado, ele é comparado com a URI da requisição. Se os dois corresponderem exatamente, o bloco location é selecionado para lidar com a requisição imediatamente.
- Se não houver locations que correspondam exatamente à comparação do modificador =, o Nginx prossegue para avaliar os prefixos que não são exatos. Assim que determinar o location de prefixo mais longo que corresponde à URI da requisição, ele realizará as seguintes avaliações:
- Se o location com a correspondência de prefixo mais longa usar o modificador ^~, este location será escolhido imediatamente.
- Se o location com o prefixo mais longo não usar o modificador ^~, a correspondência é retida brevemente pelo Nginx para permitir que o foco da busca mude.
- Assim que a correspondência de location de prefixo mais longo é encontrada e armazenada, o Nginx muda para a avaliação de locations de expressão regular. Isso inclui correspondências sensíveis e insensíveis a maiúsculas e minúsculas (case-sensitive e case-insensitive). Se o location de prefixo correspondente mais longo tiver quaisquer locations regulares dentro dele, o Nginx reformulará a lista para colocá-los perto do topo da lista de locations. A primeira expressão da lista reordenada que corresponder à URI de uma requisição será o location escolhido para atender à requisição.
- Se não forem encontradas expressões regulares para satisfazer a RI da requisição, o location armazenado anteriormente será escolhido para processar a requisição.
O Nginx prioriza correspondências de expressão regular em detrimento daquelas com prefixo preferencial por padrão. Ele, no entanto, avalia os locations de prefixo primeiro, para que a parte administradora possa anular essa tendência com os modificadores = e ^~.
Outro ponto importante a se destacar é que, embora os locations de prefixo sejam normalmente baseados na correspondência mais específica e longa encontrada, a verificação de expressão regular é interrompida assim que a primeira correspondência é identificada. Isso significa que o posicionamento dentro da configuração tem implicações reais para os locations de expressão regular.
Um ponto final a ser abordado é que as correspondências de expressão regular dentro da correspondência com o prefixo mais longo essencialmente furarão a fila durante as avaliações de location do Nginx. Estas serão posicionadas no topo da lista e avaliadas antes de outras expressões regulares.
Quando ocorre o salto para outros locations nas avaliações de blocos location?
Normalmente, uma vez que uma requisição é avaliada e um bloco location para tratá-la é selecionado, ela será tratada inteiramente dentro desse contexto. Isso significa que apenas as diretivas herdadas e os locations selecionados são os determinantes no processamento da requisição, sem qualquer entrada de blocos location irmãos.
Embora esta seja uma diretiva geral que permite o design previsível de blocos location, às vezes certas diretivas dentro do location também podem acionar uma nova busca. Em outras palavras, a regra de 'apenas um bloco location' tem algumas exceções. Essas exceções podem não se alinhar com a expectativa dos blocos location. Portanto, elas podem não tratar a requisição como esperado.
Esses redirecionamentos internos podem acabar se manifestando devido a algumas diretivas, incluindo:
- index
- rewrite
- error_page
- try_files
Se você usar a diretiva index, isso sempre resultará em um redirecionamento interno durante o tratamento da requisição. Embora a busca por correspondências de localização normalmente encerre a execução do algoritmo para acelerar o processo de seleção, se a correspondência de localização encontrada for um diretório, a requisição provavelmente será redirecionada para outra localização para ser formalmente processada.
Por exemplo, a primeira localização a seguir corresponde a uma URI de requisição de /exact. No entanto, para processar a requisição, a diretiva index que o bloco de localização herda redireciona a requisição para um bloco secundário:

Para esse cenário, se a execução precisar permanecer dentro do bloco primário, outro esquema precisará processar a requisição para o diretório. Uma maneira de fazer isso é configurar um index inválido para o bloco em questão e ativar o auto index em seu lugar:

Embora esse método possa funcionar em alguns casos, ele não é, em geral, aplicável na prática na maioria dos contextos. Uma correspondência exata de diretório pode ser útil para situações em que a requisição precisa ser reescrita. Isso acionará uma busca de localização totalmente nova.
Outra diretiva que pode ser usada para reavaliar o local de processamento é a diretiva try_files. Ela diz ao Nginx para verificar especificamente se um conjunto nomeado de arquivos ou diretórios existe, sendo o último critério de busca a URI para a qual o Nginx deve redirecionar internamente.
Vamos pensar na seguinte configuração:

Se houver uma requisição para /blahblah, la primeira localização a receberá. Não encontrar o arquivo blahblah no diretório /var/www/main acionará uma busca subsequente por blahblah.html. Em seguida, procurará por um subdiretório chamado blahblah no diretório /var/www/main. Se todas essas verificações falharem, ele redirecionará para /fallback/index.html. Isso acionará outra busca de localização que outro bloco de localização capturará. Então, ele processará o arquivo /var/www/another/fallback/index.html.
Outra diretiva que resulta em um redirecionamento para outro bloco de localização é a diretiva rewrite. O Nginx buscará uma nova localização correspondente com base no resultado da diretiva rewrite quando o último parâmetro for usado. Se o último exemplo for modificado para incluir agora esta diretiva rewrite, torna-se evidente que a requisição pode ser redirecionada para outra localização sem que a diretiva try_files seja implementada:

Para este exemplo, la requisição para /rewrite/hello será tratada inicialmente pela primeira localização. Depois de ser reescrita para /hello, uma busca de localização secundária será acionada. Ela corresponderá à primeira localização. Será processada pela diretiva try_file, potencialmente revertendo para /fallback/index.html se não produzir resultados.
Se uma requisição for feita para /rewrite/fallback/hello, no entanto, uma correspondência com o primeiro bloco será encontrada. Assim, a reescrita será processada novamente, mas desta vez produzirá /fallback/hello como resultado. A requisição será processada em outro bloco de localização.
Situações semelhantes ocorrem quando você usa a diretiva return para enviar códigos de status 301 ou 302. A única diferença é que uma nova requisição é gerada e se manifesta em um redirecionamento muito óbvio. Da mesma forma, isso pode ocorrer com a diretiva rewrite quando você aplica as flags permanent ou redirect.
Outra diretiva que pode levar a redirecionamentos internos semelhantes aos do try_again é a diretiva error_page. Você pode usá-la quando encontrar códigos de erro específicos no processamento. Quando uma diretiva try_files está definida, a diretiva error_page provavelmente nunca será executada. Isso ocorre porque essa diretiva lidará com todo o ciclo de vida da requisição.
Vamos considerar o seguinte exemplo:

Neste caso, cada requisição será processada pelo primeiro bloco que serve arquivos de /var/www/main. Isso não se aplica àquelas requisições que começam com /another. Mas se um arquivo não for encontrado, haverá um redirecionamento interno iniciado para /another/whoops/html. Isso levará a outra busca de localização. Por sua vez, direcionará a requisição para um bloco secundário, com esse arquivo sendo tratado a partir de /var/www/another/whoops.html.
Como é evidente, a compreensão das situações em que o Nginx acionará uma nova busca de localização pode ajudar a prever melhor o comportamento do sistema quando as requisições estiverem sendo processadas.
Conclusão
O trabalho dos administradores torna-se imensamente mais simples quando eles compreendem os métodos pelos quais o Nginx trata as requisições dos clientes. Isso permite que os administradores determinem para qual bloco de servidor a requisição irá. Eles também podem determinar o bloco de localização que será selecionado com base na URI da requisição. De modo geral, isso também concede aos administradores a capacidade de rastrear os contextos aplicados pelo Nginx ao tratar cada requisição.
Finalmente, você pode dar uma olhada nos outros tutoriais em nosso blog focados no Nginx. Eles ajudarão você a se beneficiar melhor de um dos servidores web mais populares do mundo:
- O Mundo dos Servidores Web: Apache vs. Nginx
- Como Proteger o Nginx com Let’s Encrypt no Ubuntu 20.04
- Automatizar as Renovações de Certificados SSL do LetsEncrypt para o Nginx
- Implantando Laravel, Nginx e MySQL com Docker Compose
Feliz Computação!
Comentários
Nenhum comentário ainda. Seja o primeiro.