One place for hosting & domains

      algoritmos

      Información sobre algoritmos de selección de bloques de servidores y ubicación de Nginx


      Introducción

      Nginx es uno de los servidores web más populares del mundo. Puede manejar correctamente altas cargas con muchas conexiones de clientes concurrentes y puede funcionar fácilmente como servidor web, servidor de correo o servidor de proxy inverso.

      En esta guía, explicaremos algunos de los detalles en segundo plano que determinan cómo Nginx procesa las solicitudes de los clientes. Entender estas ideas puede ayudar a despejar las incógnitas sobre el diseño de bloques de servidores y ubicación, y puede hacer que el manejo de las solicitudes parezca menos impredecible.

      Configuraciones de bloques de Nginx

      Nginx divide de forma lógica las configuraciones destinadas a entregar distintos contenidos en bloques, que conviven en una estructura jerárquica. Cada vez que se realiza una solicitud de cliente, Nginx inicia un proceso para determinar qué bloques de configuración deben usarse para gestionar la solicitud. Este proceso de decisión es lo que explicaremos en esta guía.

      Los bloques principales que explicaremos son el bloque de servidor y el bloque de ubicación.

      Un bloque de servidor es un subconjunto de la configuración de Nginx que define un servidor virtual utilizado para gestionar las solicitudes de un tipo definido. Los administradores suelen configurar varios bloques de servidores y decidir qué bloque debe gestionar cada conexión según el nombre de dominio, el puerto y la dirección IP solicitados.

      Un bloque de ubicación reside dentro de un bloque de servidor y se utiliza para definir la manera en que Nginx debe gestionar las solicitudes para diferentes recursos y URI para el servidor principal. El espacio URI puede subdividirse de la manera que el administrador quiera utilizando estos bloques. Es un modelo extremadamente flexible.

      Cómo decide Nginx qué bloque de servidor gestionará una solicitud

      Dado que Nginx permite que el administrador defina varios bloques de servidores que funcionan como instancias de servidores web virtuales independientes, necesita un procedimiento para determinar cuál de estos bloques de servidores se utilizará para satisfacer una solicitud.

      Lo hace mediante un sistema definido de comprobaciones que se utilizan para encontrar la mejor coincidencia posible. Las principales directivas de bloques de servidores de las que se ocupa Nginx durante este proceso son la directiva listen y la directiva server_name.

      Análisis de la directiva “listen” para encontrar posibles coincidencias

      Primero, Nginx examina la dirección IP y el puerto de la solicitud. Luego, lo compara con la directiva listen de cada servidor para crear una lista de los bloques de servidores que pueden resolver la solicitud.

      La directiva listen generalmente define la dirección IP y el puerto a los que responderá el bloque de servidor. De manera predeterminada, cualquier bloque de servidor que no incluya una directiva listen recibe los parámetros de escucha 0.0.0.0:80 (o 0.0.0.0:8080 si Nginx está siendo ejecutado por un usuario no root regular). Eso permite que estos bloques respondan a las solicitudes en cualquier interfaz en el puerto 80, pero este valor predeterminado no afecta demasiado al proceso de selección de servidores.

      La directiva listen puede establecerse para las siguientes características:

      • Una combinación de dirección IP y puerto.
      • Una dirección IP individual que escuchará en el puerto 80 de manera predeterminada.
      • Un puerto individual que escuchará cada interfaz en ese puerto.
      • La ruta al socket Unix.

      La última opción, por lo general, solo tendrá implicaciones al pasar solicitudes entre distintos servidores.

      Cuando intente determinar a qué bloque de servidor enviar una solicitud, Nginx tratará primero de decidir según la especificidad de la directiva listen usando las siguientes reglas:

      • Nginx traduce todas las directivas listen “incompletas” sustituyendo los valores que faltan por sus valores predeterminados para que cada bloque pueda evaluarse por su dirección IP y su puerto. Algunos ejemplos de estas traslaciones son:
        • Un bloque sin directiva listen utiliza el valor 0.0.0.0:80.
        • Un bloque establecido en una dirección IP 111.111.111.111 sin puerto se convierte en 111.111.111.111:80.
        • Un bloque establecido en el puerto 8888 sin dirección IP se convierte en 0.0.0.0:8888.
      • Luego, Nginx intenta recopilar una lista de los bloques de servidores que coinciden con la solicitud más específicamente basada en la dirección IP y el puerto. Eso significa que cualquier bloque que esté usando de forma funcional 0.0.0.0 como su dirección IP (para coincidir con cualquier interfaz), no se seleccionará si hay bloques coincidentes que enumeran una dirección IP específica. En todo caso, el puerto debe coincidir con exactitud.
      • Si solo hay una coincidencia más específica, ese bloque de servidor se utilizará para atender la solicitud. Si hay varios bloques de servidores con el mismo nivel de coincidencia de especificidad, Nginx comienza a evaluar la directiva server_name de cada bloque de servidores.

      Es importante entender que Nginx solo evaluará la directiva server_name cuando necesite distinguir entre bloques de servidores que coinciden con el mismo nivel de especificidad en la directiva listen. Por ejemplo, si example.com está alojado en el puerto 80 de 192.168.1.10, una solicitud para example.com siempre será atendida por el primer bloque de este ejemplo, a pesar de la directiva server_name en el segundo bloque.

      server {
          listen 192.168.1.10;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name example.com;
      
          . . .
      
      }
      

      En caso de que más de un bloque de servidor coincida con la misma especificidad, el siguiente paso es verificar la directiva server_name.

      Análisis de la directiva “server_name” para elegir una coincidencia

      Luego, para evaluar más a fondo las solicitudes que tienen directivas listen igualmente específicas, Nginx verifica el encabezado “Host” de la solicitud. Ese valor contiene el dominio o la dirección IP que el cliente estaba intentando alcanzar.

      Nginx intenta encontrar la mejor coincidencia para el valor que encuentra al ver la directiva server_name dentro de cada uno de los bloques de servidores que aún son candidatos a selección. Nginx lo evalúa usando la siguiente formula:

      • Nginx intentará primero encontrar un bloque de servidor con un server_name que coincida con el valor en el encabezado “Host” de la solicitud de manera exacta. Si lo encuentra, ese bloque asociado se utilizará para atender la solicitud. Si se encuentran varias coincidencias exactas, se utiliza la primera coincidencia.
      • Si no se encuentra ninguna coincidencia exacta, Nginx intentará encontrar un bloque de servidor con un server_name que coincida usando un comodín inicial (indicado por un * al principio del nombre en la configuración). Si lo encuentra, ese bloque se utilizará para atender la solicitud. Si se encuentran varias coincidencias, la coincidencia más larga se utilizará para atender la solicitud.
      • Si no se encuentra ninguna coincidencia con el comodín inicial, Nginx buscará entonces un bloque de servidor con un server_name que coincida usando un comodín final (indicado por un nombre de servidor que termina con un * en la configuración). Si lo encuentra, ese bloque se utilizará para atender la solicitud. Si se encuentran varias coincidencias, la coincidencia más larga se utilizará para atender la solicitud.
      • Si no se encuentra ninguna coincidencia usando un comodín final, Nginx evalúa los bloques de servidor que definen el server_name usando expresiones regulares (indicadas por un ~ antes del nombre). El primer server_name con una expresión regular que coincida con el encabezado “Host” se utilizará para atender la solicitud.
      • Si no se encuentra ninguna coincidencia de expresión regular, Nginx selecciona el bloque de servidor predeterminado para esa dirección IP y ese puerto.

      Cada combinación de dirección IP/puerto tiene un bloque de servidor predeterminado que se utilizará cuando no se pueda determinar un curso de acción con los métodos anteriores. En el caso de una combinación de dirección IP y puerto, será el primer bloque de la configuración o el bloque que contiene la opción default_server como parte de la directiva listen (que anularía el primer algoritmo encontrado). Solo puede haber una declaración default_server por cada combinación de dirección IP y puerto.

      Ejemplos

      Si se define un server_name que coincida exactamente con el valor de encabezado “Host”, se selecciona ese bloque de servidor para procesar la solicitud.

      En este ejemplo, si el encabezado “Host” de la solicitud se estableció en “host1.example.com”, se seleccionaría el segundo servidor:

      server {
          listen 80;
          server_name *.example.com;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name host1.example.com;
      
          . . .
      
      }
      

      Si no se encuentra ninguna coincidencia exacta, Nginx comprueba entonces si hay un server_name con un comodín inicial que coincida. Se seleccionará la coincidencia más larga que comience con un comodín para satisfacer la solicitud.

      En este ejemplo, si la solicitud tuviera un encabezado “Host” de “www.example.org”, se seleccionaría el segundo bloque de servidor:

      server {
          listen 80;
          server_name www.example.*;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name *.example.org;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name *.org;
      
          . . .
      
      }
      

      Si no se encuentra ninguna coincidencia con un comodín inicial, Nginx verá si existe una coincidencia usando un comodín al final de la expresión. En este punto, se seleccionará la coincidencia más larga que termine con un comodín para atender la solicitud.

      Por ejemplo, si la solicitud tiene un encabezado “Host” establecido en “www.example.com”, se seleccionará el tercer bloque de servidor:

      server {
          listen 80;
          server_name host1.example.com;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name example.com;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name www.example.*;
      
          . . .
      
      }
      

      Si no se encuentran coincidencias con los comodines, Nginx pasará a intentar coincidir con las directivas server_name que utilicen expresiones regulares. Se seleccionará la primera expresión regular que coincida para responder a la solicitud.

      Por ejemplo, si el encabezado “Host” de la solicitud está configurado en “www.example.com”, se seleccionará el segundo bloque de servidor para satisfacer la solicitud:

      server {
          listen 80;
          server_name example.com;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name ~^(www|host1).*.example.com$;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name ~^(subdomain|set|www|host1).*.example.com$;
      
          . . .
      
      }
      

      Si ninguno de los pasos anteriores puede satisfacer la solicitud, la solicitud se pasará al servidor predeterminado para la dirección IP y el puerto que coincidan.

      Bloques de ubicación que coinciden

      De forma similar al proceso que utiliza Nginx para seleccionar el bloque del servidor que procesará una solicitud, Nginx también tiene un algoritmo establecido para decidir qué bloque de ubicación dentro del servidor se utilizará para gestionar las solicitudes.

      Sintaxis de bloques de ubicación

      Antes de ver cómo Nginx decide qué bloque de ubicación utilizar para gestionar las solicitudes, repasemos parte de la sintaxis que se podría encontrar en las definiciones de los bloques de ubicación. Los bloques de ubicación se encuentran dentro de los bloques de servidor (u otros bloques de ubicación) y se utilizan para decidir cómo procesar la URI de la solicitud (la parte de la solicitud que viene después del nombre de dominio o la dirección IP/puerto).

      Los bloques de ubicación generalmente tienen la siguiente forma:

      location optional_modifier location_match {
      
          . . .
      
      }
      

      location_match que aparece arriba define contra qué debe comprobar Nginx la URI de la solicitud. La existencia o la inexistencia del modificador en el ejemplo anterior afecta a la forma en que Nginx intenta hacer coincidir el bloque de ubicación. Los modificadores que se muestran abajo harán que el bloque de ubicación asociado se interprete de la siguiente manera:

      • (none): si no hay modificadores, la ubicación se interpreta como una coincidencia de prefijo. Eso significa que la ubicación dada coincidirá con el principio del URI de la solicitud para determinar una coincidencia.
      • =: si se utiliza un signo igual, este bloque se considerará coincidente si el URI de la solicitud coincide exactamente con la ubicación indicada.
      • ~: si hay una tilde de la eñe, esta ubicación se interpretará como una coincidencia de expresión regular que distingue entre mayúsculas y minúsculas.
      • ~*: si se utiliza un modificador de tilde de la eñe y asterisco, el bloque de ubicación se interpretará como una coincidencia de expresión regular que no distingue entre mayúsculas y minúsculas.
      • ^~: si hay un modificador de acento circunflejo y tilde de la eñe, y si este bloque se selecciona como la mejor coincidencia de expresión no regular, no se realizará la coincidencia de expresión regular.

      Ejemplos que demuestran la sintaxis del bloque de ubicación

      Como ejemplo de concordancia de prefijos, se puede seleccionar el siguiente bloque de ubicación para responder a los URI de solicitud que tienen el aspecto /site, /site/page1/index.html o /site/index.html:

      location /site {
      
          . . .
      
      }
      

      Para una demostración de la coincidencia exacta del URI de la solicitud, este bloque siempre se utilizará para responder a un URI de solicitud que tenga el aspecto /page1. No se utilizará para responder a un URI de solicitud /page1/index.html. Tenga en cuenta que, si se selecciona este bloque y la solicitud se cumple usando una página de índice, se realizará un redireccionamiento interno a otra ubicación que será la que realmente gestione la solicitud:

      location = /page1 {
      
          . . .
      
      }
      

      Como ejemplo de una ubicación que debe interpretarse como una expresión regular que distingue entre mayúsculas y minúsculas, este bloque puede usarse para gestionar las solicitudes de /tortoise.jpg, pero no para /FLOWER.PNG:

      location ~ .(jpe?g|png|gif|ico)$ {
      
          . . .
      
      }
      

      A continuación, se muestra un bloque que permitiría una coincidencia sin distinción ente mayúsculas y minúsculas similar al que se mostró anteriormente. Este bloque podría gestionar /tortoise.jpg y /FLOWER.PNG:

      location ~* .(jpe?g|png|gif|ico)$ {
      
          . . .
      
      }
      

      Por último, este bloque evitaría que se produjera una coincidencia de expresión regular si se determina que es la mejor coincidencia de expresión no regular. Podría gestionar las solicitudes de /costumes/ninja.html:

      location ^~ /costumes {
      
          . . .
      
      }
      

      Como ve, los modificadores indican cómo debe interpretarse el bloque de ubicación. Sin embargo, esto no nos indica el algoritmo que utiliza Nginx para decidir a qué bloque de ubicación enviar la solicitud. Lo repasaremos a continuación.

      Cómo elige Nginx qué ubicación utilizar para gestionar las solicitudes

      Nginx elige la ubicación que se utilizará para atender una solicitud de forma similar a cómo selecciona un bloque de servidor. Se ejecuta a través de un proceso que determina el mejor bloque de ubicación para cualquier solicitud. Entender este proceso es un requisito crucial para poder configurar Nginx de forma fiable y precisa.

      Teniendo en cuenta los tipos de declaraciones de ubicación que hemos descrito anteriormente, Nginx evalúa los posibles contextos de ubicación comparando el URI de solicitud con cada una de las ubicaciones. Lo hace mediante el siguiente algoritmo:

      • Nginx comienza comprobando todas las coincidencias de ubicación basadas en prefijos (todos los tipos de ubicación que no implican una expresión regular). Comprueba cada ubicación con el URI completo de la solicitud.
      • Primero, Nginx busca una coincidencia exacta. Si se encuentra un bloque de ubicación que utilice el modificador = y que coincida con el URI de la solicitud de manera exacta, este bloque de ubicación se selecciona inmediatamente para atender la solicitud.
      • Si no se encuentran coincidencias exactas con bloques de ubicación (con el modificador =), Nginx pasa a evaluar los prefijos no exactos. Descubre la ubicación del prefijo más largo que coincide con el URI de la solicitud dada que luego evalúa de la siguiente manera:
        • Si la ubicación del prefijo más largo que coincide tiene el modificador ^~, Nginx finalizará inmediatamente su búsqueda y seleccionará esta ubicación para atender la solicitud.
        • Si la ubicación del prefijo más largo que coincide no utiliza el modificador ^~, Nginx almacena la coincidencia de momento para poder cambiar el enfoque de la búsqueda.
      • Una vez que se determina y almacena la ubicación del prefijo más largo que coincide, Nginx procede a evaluar las ubicaciones de expresiones regulares (las que distinguen entre mayúsculas y minúsculas, y las que no). Si hay alguna ubicación de expresiones regulares dentro de la ubicación del prefijo más largo que coincide, Nginx la pondrá a la parte superior de su lista de ubicaciones de regex para comprobar. Luego, Nginx intentará hacer coincidir con las ubicaciones de expresiones regulares de forma secuencial. La primera ubicación de expresión regular que coincida con el URI de la solicitud se seleccionará inmediatamente para atender la solicitud.
      • Si no se encuentra ninguna ubicación de expresión regular que coincida con el URI de la solicitud, se seleccionará la ubicación de prefijo previamente almacenado para atender la solicitud.

      Es importante entender que, de manera predeterminada, Nginx atenderá las coincidencias de expresiones regulares con mayor preferencia en comparación con las coincidencias de prefijos. Sin embargo, primero evalúa las ubicaciones de prefijos, lo que permite al administrador anular esta tendencia especificando las ubicaciones usando los modificadores = y ^~.

      También es importante tener en cuenta que, aunque las ubicaciones de prefijos generalmente se seleccionan según la coincidencia más larga y específica, la evaluación de la expresión regular se detiene cuando se encuentra la primera ubicación que coincida. Eso significa que el posicionamiento dentro de la configuración tiene amplias implicaciones para las ubicaciones de expresiones regulares.

      Por último, es importante entender que las coincidencias de expresiones regulares dentro de la coincidencia del prefijo más largo “saltarán la línea” cuando Nginx evalúe las ubicaciones de regex. Estas se evaluarán en orden, antes de que se consideren las demás coincidencias de expresiones regulares. Maxim Dounin, un desarrollador de Nginx increíblemente atento, explica en esta publicación esta parte del algoritmo de selección.

      ¿Cuándo salta la evaluación del bloque de ubicaciones a otras ubicaciones?

      En general, cuando se selecciona un bloque de ubicación para atender una solicitud, la solicitud se gestiona completamente dentro de ese contexto a partir de ese momento. Solo la ubicación seleccionada y las directivas heredadas determinan cómo se procesa la solicitud, sin interferencia de los bloques de ubicación hermanos.

      Aunque esta es una regla general que le permitirá diseñar sus bloques de ubicación de una manera predecible, es importante darse cuenta de que, a veces, una nueva búsqueda de ubicación se activa por ciertas directivas dentro de la ubicación seleccionada. Las excepciones a la regla “solo un bloque de ubicación” pueden tener implicaciones sobre cómo se atiende realmente la solicitud y pueden no coincidir con las expectativas que tenía al diseñar sus bloques de ubicación.

      Algunas directivas que pueden dar como resultado este tipo de redireccionamiento interno son:

      • index
      • try_files
      • rewrite
      • error_page

      Las repasaremos brevemente.

      La directiva index siempre conduce a un redireccionamiento interno si se utiliza para gestionar la solicitud. Las coincidencias de ubicación exactas se utilizan a menudo para acelerar el proceso de selección, terminando inmediatamente la ejecución del algoritmo. Sin embargo, si realiza una coincidencia de ubicación exacta que sea un directorio, hay una buena probabilidad de que la solicitud sea redirigida a una ubicación diferente para el procesamiento real.

      En este ejemplo, la primera ubicación coincide con un URI de solicitud de /exact, pero, para gestionar la solicitud, la directiva index heredada por el bloque inicia un redireccionamiento interno al segundo bloque:

      index index.html;
      
      location = /exact {
      
          . . .
      
      }
      
      location / {
      
          . . .
      
      }
      

      En el caso anterior, si realmente necesita que la ejecución permanezca en el primer bloque, tendrá que encontrar un método diferente para satisfacer la solicitud al directorio. Por ejemplo, podría establecer un index no válido para ese bloque y activar autoindex:

      location = /exact {
          index nothing_will_match;
          autoindex on;
      }
      
      location  / {
      
          . . .
      
      }
      

      Esta es una forma de evitar que un index cambie de contexto, pero probablemente no sea útil para la mayoría de las configuraciones. Generalmente, una coincidencia exacta en directorios puede ser útil para acciones como reescribir la solicitud (lo que también genera una nueva búsqueda de ubicación).

      Otro caso en que se puede reevaluar la ubicación del procesamiento es con la directiva try_files. Esa directiva le indica a Nginx que busque la existencia de un conjunto determinado de archivos o directorios. El último parámetro puede ser un URI al que Nginx realizará un redireccionamiento interno.

      Analice la siguiente configuración:

      root /var/www/main;
      location / {
          try_files $uri $uri.html $uri/ /fallback/index.html;
      }
      
      location /fallback {
          root /var/www/another;
      }
      

      En el ejemplo anterior, si se realiza una solicitud para /blahblah, la primera ubicación obtendrá inicialmente la solicitud. Intentará encontrar un archivo llamado blahblah en el directorio /var/www/main. Si no puede encontrar ninguno, procederá a buscar un archivo llamado blahblah.html. Luego, intentará buscar un directorio llamado blahblah/ dentro del directorio /var/www/main. Si todos esos intentos fallan, se redirigirá a /fallback/index.html. Eso activará otra búsqueda de ubicación que el segundo bloque de ubicación capturará. Y atenderá el archivo /var/www/another/fallback/index.html.

      Otra directiva que puede pasar el bloque de ubicación es la directiva rewrite. Cuando utiliza el último parámetro con la directiva rewrite o cuando no utiliza ningún parámetro, Nginx buscará una nueva ubicación que coincida según los resultados de la reescritura.

      Por ejemplo, si modificamos el último ejemplo para incluir una reescritura, podemos ver que la solicitud a veces se pasa directamente a la segunda ubicación sin depender de la directiva try_files:

      root /var/www/main;
      location / {
          rewrite ^/rewriteme/(.*)$ /$1 last;
          try_files $uri $uri.html $uri/ /fallback/index.html;
      }
      
      location /fallback {
          root /var/www/another;
      }
      

      En el ejemplo de arriba, el primer bloque de ubicación gestionará inicialmente una solicitud de /rewriteme/hello. Se reescribirá a /hello y se buscará una ubicación. En este caso, volverá a coincidir con la primera ubicación y se procesará mediante try_files de manera habitual, probablemente regresando a /fallback/index.html si no se encuentra nada (usando el redireccionamiento interno de try_files que mencionamos antes).

      Sin embargo, si se realiza una solicitud para /rewriteme/fallback/hello, el primer bloque volverá a coincidir. Se aplicará la reescritura de nuevo, dando como resultado /fallback/hello esta vez. La solicitud se atenderá desde el segundo bloque de ubicación.

      Algo similar sucede con la directiva return cuando se envían los códigos de estado 301 o 302. La diferencia en este caso es que da como resultado una solicitud completamente nueva en forma de un redireccionamiento visible desde el exterior. Eso mismo puede suceder con la directiva rewrite cuando se utilizan los indicadores redirect o permanent. Sin embargo, estas búsquedas de ubicación no deberían ser inesperadas, ya que los redireccionamientos visibles externamente siempre dan como resultado una nueva solicitud.

      La directiva error_page puede dar como resultado un redireccionamiento interno similar al que se creó con try_files. Esa directiva se utiliza para definir lo que debe suceder cuando se encuentran ciertos códigos de estado. Esto probablemente nunca se ejecutará si se establece try_files, ya que esa directiva maneja todo el ciclo de vida de una solicitud.

      Analice este ejemplo:

      root /var/www/main;
      
      location / {
          error_page 404 /another/whoops.html;
      }
      
      location /another {
          root /var/www;
      }
      

      Todas las solicitudes (aparte de las que comienzan con /another) se gestionarán mediante el primer bloque, que atenderá archivos desde /var/www/main. Sin embargo, si no se encuentra un archivo (un estado 404), se producirá un redireccionamiento interno a /another/whoops.html, lo que llevará a una nueva búsqueda de ubicación que finalmente aterrizará en el segundo bloque. Este archivo se atenderá desde /var/www/another/whoops.html.

      Como se puede ver, entender las circunstancias en que Nginx activa una nueva búsqueda de ubicación puede ayudar a predecir el comportamiento que habrá cuando se hagan solicitudes.

      Conclusión

      Entender las formas en que Nginx procesa las solicitudes de los clientes puede hacer que su trabajo como administrador sea mucho más fácil. Podrá saber qué bloque de servidor seleccionará Nginx según cada solicitud del cliente. También podrá saber cómo se seleccionará el bloque de ubicación según el URI de la solicitud. En general, saber la forma en que Nginx selecciona los diferentes bloques le permitirá rastrear los contextos que Nginx aplicará para atender cada solicitud.



      Source link

      Entendendo o servidor Nginx e os algoritmos de seleção de blocos de localização


      Introdução

      O Nginx é um dos servidores Web mais populares do mundo. Ele é capaz de lidar com grandes cargas com muitas conexões de cliente simultâneas, e pode funcionar facilmente como um servidor Web, um servidor de e-mail ou um servidor de proxy reverso.

      Neste guia, vamos discutir alguns dos detalhes dos bastidores que determinam como o Nginx processa as solicitações de clientes. A compreensão dessas ideias pode ajudar a eliminar a necessidade de suposições ao projetar servidores e blocos de localização e pode tornar o processamento de solicitações menos imprevisível.

      Configurações em bloco do Nginx

      O Nginx divide logicamente as configurações destinadas a atender diferentes conteúdo em blocos, que operam em uma estrutura hierárquica. Cada vez que uma solicitação de cliente é feita, o Nginx começa um processo para determinar quais blocos de configuração devem ser usados para lidar com a solicitação. Este processo de decisão é o que discutiremos neste guia.

      Os principais blocos que discutiremos são o bloco de servidor e o bloco de localização.

      Um bloco de servidor é um subconjunto da configuração do Nginx que define um servidor virtual usado para manusear solicitações de um determinado tipo. Os administradores geralmente configuram vários blocos de servidor e decidem qual bloco deve lidar com qual conexão com base no nome de domínio, porta e endereço IP solicitados.

      Um bloco de localização fica dentro de um bloco de servidor e é usado para definir como o Nginx deve manusear solicitações para diferentes recursos e URIs para o servidor pai. O espaço URI pode ser subdividido da maneira que o administrador preferir usando esses blocos. Ele é um modelo extremamente flexível.

      Como o Nginx decida qual bloco de servidor irá manusear uma solicitação

      Como o Nginx permite que o administrador defina vários blocos de servidor que funcionam como instâncias de servidor Web separadas, ele precisa de um procedimento para determinar qual desses blocos de servidor será usado para satisfazer uma solicitação.

      Ele faz isso através de um sistema definido de verificações que são usadas para encontrar a melhor combinação possível. As principais diretivas do bloco de servidor com as quais o Nginx se preocupa durante esse processo são a diretiva listen (escuta) e a diretiva server_name (nome do servidor).

      Analisando a diretiva “listen” para encontrar possíveis correspondências

      Primeiramente, o Nginx analisa o endereço IP e a porta da solicitação. Ele compara esses valores com a diretiva listen de cada servidor para construir uma lista dos blocos de servidor que podem resolver a solicitação.

      A diretiva listen tipicamente define quais endereços IP e portas que serão respondidos pelo bloco de servidor. Por padrão, qualquer bloco de servidor que não inclua uma diretiva listen recebe os parâmetros de escuta de 0.0.0.0:80 (ou 0.0.0.0:8080 se o Nginx estiver sendo executado por um usuário normal, não root). Isso permite que esses blocos respondam a solicitações em qualquer interface na porta 80, mas esse valor padrão não possui muito peso dentro do processo de seleção de servidor.

      A diretiva listen pode ser definida como:

      • Uma combinação de endereço IP/porta.
      • Um endereço IP solto que então escutará na porta padrão 80.
      • Uma porta solta que escutará todas as interfaces nessa porta.
      • O caminho para um soquete do Unix.

      Geralmente, a última opção só terá implicações ao passar solicitações entre servidores diferentes.

      Ao tentar determinar para qual bloco de servidor enviar uma solicitação, o Nginx primeiro tentará decidir com base na especificidade da diretiva listen usando as seguintes regras:

      • O Nginx traduz todas as diretivas listen “incompletas” substituindo valores que estão faltando pelos seus valores padrão para que cada bloco possa ser avaliado por seu endereço IP e porta. Alguns exemplos dessas traduções são:
        • Um bloco sem a diretiva listen usa o valor 0.0.0.0:80.
        • Um bloco definido para um endereço IP 111.111.111.111 sem porta se torna 111.111.111.111:80
        • Um bloco definido para a porta 8888 sem endereço IP torna-se 0.0.0.0:8888
      • Em seguida, o Nginx tenta coletar uma lista dos blocos de servidor que correspondem à solicitação, mais especificamente com base no endereço IP e porta. Isso significa que qualquer bloco que esteja funcionalmente usando 0.0.0 0 como endereço IP (para corresponder a qualquer interface), não será selecionado se houver blocos correspondentes que listam um endereço IP específico. Em todo caso, a porta deve ser exatamente a mesma.
      • Se houver apenas uma correspondência mais específica, o bloco de servidor será usado para atender a solicitação. Se houver vários blocos de servidor com o mesmo nível de especificidade correspondente, o Nginx então começa a avaliar a diretiva server_name de cada bloco de servidor.

      É importante entender que o Nginx avaliará apenas a diretiva server_name quando precisar distinguir entre blocos de servidor que correspondem ao mesmo nível de especificidade na diretiva listen. Por exemplo, se o example.com for hospedado na porta 80 de 192.168.1.10, uma solicitação para example.com será sempre atendida pelo primeiro bloco neste exemplo, apesar da diretiva server_name no segundo bloco.

      server {
          listen 192.168.1.10;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name example.com;
      
          . . .
      
      }
      

      Caso mais de um bloco de servidor corresponda com o mesmo nível de especificidade, o próximo passo é verificar a diretiva server_name.

      Analisando a diretiva “server_name” para escolher uma correspondência

      Em seguida, para avaliar ainda mais as solicitações que possuem diretivas listen igualmente específicas, o Nginx verifica o cabeçalho “Host” da solicitação. Esse valor possui o domínio ou endereço IP que o cliente estava tentando realmente alcançar.

      O Nginx tenta achar a melhor correspondência para o valor que ele encontra olhando a diretiva server_name dentro de cada um dos blocos de servidor que ainda são candidatos a seleção. O Nginx avalia eles usando a seguinte fórmula:

      • O Nginx primeiro tentará encontrar um bloco de servidor com um server_name que corresponda exatamente ao valor no cabeçalho “Host” da solicitação. Se for encontrado, o bloco associado será usado para atender à solicitação. Se mais de uma correspondência exata for encontrada, a primeira é usada.
      • Se nenhuma correspondência exata for encontrada, o Nginx então tentará encontrar um bloco de servidor com um server_name correspondente usando um curinga inicial (indicado por um * no início do nome na configuração). Se for encontrado, o bloco será usado para atender à solicitação. Se várias correspondências forem encontradas, a correspondência mais longa será usada para atender à solicitação.
      • Se nenhuma correspondência for encontrada usando um curinga inicial, o Nginx então procura um bloco de servidor com um server_name correspondente usando um curinga à direita (indicado por um nome de servidor que termina com um * na configuração). Se for encontrado, o bloco será usado para atender à solicitação. Se várias correspondências forem encontradas, a correspondência mais longa será usada para atender à solicitação.
      • Se nenhuma correspondência for encontrada usando um curinga à direita, o Nginx então avalia blocos de servidor que definem o server_name usando expressões regulares (indicado por um ~ antes do nome). O primeiro server_name com uma expressão regular que corresponda ao cabeçalho “Host” será usado para atender à solicitação.
      • Se nenhuma expressão regular correspondente for encontrada, o Nginx seleciona então o bloco de servidor padrão para aquele endereço IP e porta.

      Cada combinação de endereço IP/porta possui um bloco de servidor padrão que será usado quando um plano de ação não puder ser determinado com os métodos acima. Para uma combinação de endereço IP/porta, ele será o primeiro bloco na configuração ou o bloco que contém a opção default_server como parte da diretiva listen (que iria se sobrepor ao algoritmo encontrado por primeiro). Pode haver apenas uma declaração default_server para cada combinação de endereço IP/porta.

      Exemplos

      Se houver um server_name definido que corresponda exatamente ao valor de cabeçalho “Host”, o bloco de servidor é selecionado para processar a solicitação.

      Neste exemplo, se o cabeçalho “Host” da solicitação fosse definido como ”host1.example.com”, o segundo servidor seria selecionado:

      server {
          listen 80;
          server_name *.example.com;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name host1.example.com;
      
          . . .
      
      }
      

      Se nenhuma correspondência exata for encontrada, o Nginx então verifica se há um server_name com um curinga inicial que se encaixa. A correspondência mais longa que começa com um curinga será selecionada para atender à solicitação.

      Neste exemplo, se a solicitação tivesse um cabeçalho “Host” de ”www.example.org”, o segundo bloco de servidor seria selecionado:

      server {
          listen 80;
          server_name www.example.*;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name *.example.org;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name *.org;
      
          . . .
      
      }
      

      Se nenhuma correspondência for encontrada com um curinga inicial, o Nginx então verá se uma correspondência existe usando um curinga no final da expressão. Neste ponto, a correspondência mais longa que termina com um curinga será selecionada para atender à solicitação.

      Por exemplo, se a solicitação tiver um cabeçalho “Host” definido como ”www.example.com”, o terceiro bloco de servidor será selecionado:

      server {
          listen 80;
          server_name host1.example.com;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name example.com;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name www.example.*;
      
          . . .
      
      }
      

      Se nenhuma correspondência com curingas puder ser encontrada, o Nginx então seguirá adiante para tentar corresponder o termo com as diretivas do server_name que usam expressões regulares. A primeira expressão regular correspondente será selecionada para responder à solicitação.

      Por exemplo, se o cabeçalho “Host” da solicitação for definido como ”www.example.com”, então o segundo bloco de servidor será selecionado para satisfazer a solicitação:

      server {
          listen 80;
          server_name example.com;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name ~^(www|host1).*.example.com$;
      
          . . .
      
      }
      
      server {
          listen 80;
          server_name ~^(subdomain|set|www|host1).*.example.com$;
      
          . . .
      
      }
      

      Se nenhum dos passos acima for capaz de satisfazer a solicitação, então a solicitação será passada ao servidor padrão para o endereço IP e porta correspondentes.

      Fazendo correspondência de blocos de localização

      De maneira similar ao processo que o Nginx usa para selecionar o bloco de servidor que processará uma solicitação, o Nginx também possui um algoritmo estabelecido para decidir qual bloco de localização dentro do servidor usar para manusear solicitações.

      Sintaxe do bloco de localização

      Antes de abordarmos como o Nginx decide qual bloco de localização usar para manusear solicitações, vamos analisar algumas das sintaxes que podem ser vistas nas definições de blocos de localização. Os blocos de localização localizam-se dentro de blocos de servidor (ou outros blocos de localização) e são usados para decidir como processar o URI de solicitação (a parte da solicitação que vem após o nome de domínio ou endereço IP/porta).

      Os blocos de localização geralmente possuem a seguinte forma:

      location optional_modifier location_match {
      
          . . .
      
      }
      

      O location_match no exemplo acima define com o que o Nginx deve comparar o URI de solicitação. A existência ou não existência do modificador no exemplo acima afeta a maneira como o Nginx tenta corresponder o bloco de localização. Os modificadores abaixo farão com que o bloco de localização associado seja interpretado da seguinte forma:

      • (none): se nenhum modificador estiver presente, o local é interpretado como uma correspondência por prefixo. Isso significa que o local dado será comparado ao início do URI de solicitação para determinar se há correspondência.
      • =: se um sinal de igual for usado, o bloco será considerado uma correspondência se o URI de solicitação corresponder exatamente ao local dado.
      • ~: se um modificador til estiver presente, o local será interpretado como uma correspondência de expressão regular sensível a maiúsculas e minúsculas.
      • ~*: se um modificador de til e asterisco for usado, o bloco de localização será interpretado como uma correspondência de expressão regular que não diferencia maiúsculas de minúsculas.
      • ^~: se um modificador de acento circunflexo e til estiver presente, e se este bloco for selecionado como a melhor correspondência de expressão não regular, a correspondência de expressão regular não será realizada.

      Exemplos demonstrando a sintaxe do bloco de localização

      Como um exemplo de correspondência de prefixos, o bloco de localização a seguir pode ser selecionado para responder aos URIs de solicitação que se pareçam com /site, /site/page1/index.html ou /site/index.html:

      location /site {
      
          . . .
      
      }
      

      Para demonstrar a correspondência exata de URIs de solicitação, esse bloco será sempre usado para responder a um URI de solicitação que se pareça com /page1. Ele não será usado para responder a um URI de solicitação /page1/index.html. Lembre-se de que se esse bloco for selecionado e a solicitação for realizada usando uma página de índice, um redirecionamento interno será feito para outro local. Ele será o manuseador real da solicitação:

      location = /page1 {
      
          . . .
      
      }
      

      Como um exemplo de um local que deve ser interpretado como uma expressão regular sensível a maiúsculas e minúsculas, este bloco poderia ser usado para manusear solicitações para /tortoise.jpg, mas não para /FLOWER.PNG:

      location ~ .(jpe?g|png|gif|ico)$ {
      
          . . .
      
      }
      

      Um bloco que permitiria a correspondência sem diferenciar maiúsculas de minúsculas parecido com o exemplo acima é mostrado abaixo. Aqui, tanto /tortoise.jpg quanto /FLOWER.PNG poderiam ser manuseados por este bloco:

      location ~* .(jpe?g|png|gif|ico)$ {
      
          . . .
      
      }
      

      Por fim, este bloco impediria a ocorrência de correspondências de expressão regular se ele fosse escolhido como a melhor correspondência de expressão não regular. Ele poderia manusear solicitações para /costumes/ninja.html:

      location ^~ /costumes {
      
          . . .
      
      }
      

      Como se vê, os modificadores indicam como o bloco de localização deve ser interpretado. No entanto, isso não nos informa qual algoritmo o Nginx usa para decidir para qual bloco de localização enviar a solicitação. Vamos analisar isso a seguir.

      Como o Nginx escolhe qual localização usar para processar solicitações

      O Nginx escolhe o local que será usado para atender a uma solicitação de forma semelhante a como seleciona um bloco de servidor. Ele executa um processo que determina o melhor bloco de localização para qualquer solicitação. Entender esse processo é um requisito crucial para ser capaz de configurar o Nginx de maneira confiável e precisa.

      Tendo em mente os tipos de declaração de localização que descrevemos acima, o Nginx avalia os possíveis contextos de localização comparando o URI de solicitação com cada um dos locais. Ele faz isso usando o seguinte algoritmo:

      • O Nginx começa verificando todas as correspondências de localização baseadas em prefixos (todos os tipos de localização que não envolvem expressões regulares). Ele compara cada localização com o URI de solicitação completo.
      • Primeiramente, o Nginx procura por uma correspondência exata. Se um bloco de localização usando o modificador = for encontrado para corresponder exatamente ao URI de solicitação, este bloco de localização é selecionado imediatamente para atender à solicitação.
      • Se nenhuma correspondência exata de bloco de localização (com o modificador =) for encontrada, o Nginx então passa a avaliar prefixos não exatos. Ele descobre o prefixo de correspondência mais longo para um determinado URI de solicitação, que é então avaliado da seguinte forma:
        • Se o prefixo correspondente mais longo possuir o modificador ^~, então o Nginx imediatamente terminará sua pesquisa e selecionará esse local para atender à solicitação.
        • Se o prefixo correspondente mais longo não usar o modificador ^~, a correspondência é armazenada pelo Nginx por enquanto, para que o foco da pesquisa possa mudar.
      • Após o prefixo correspondente mais longo ser determinado e armazenado, o Nginx passa a avaliar as localizações de expressões regulares (tanto as que diferenciam, quanto as que não diferenciam letras maiúsculas de minúsculas). Se houver alguma localização de expressão regular dentro da localização do prefixo correspondente mais longo, o Nginx as moverá para o topo de sua lista de localizações de regex para verificação. Em seguida, o Nginx tenta fazer a correspondência com as localizações das expressões regulares sequencialmente. A primeira localização de expressão regular que corresponder ao URI de solicitação é selecionada imediatamente para atender à solicitação.
      • Se nenhuma localização de expressão regular que corresponda ao URI de solicitação for encontrada, a localização de prefixo armazenada anteriormente será selecionada para atender à solicitação.

      É importante entender que, por padrão, o Nginx atenderá correspondências por expressão regular preferencialmente em relação a correspondências por prefixo. No entanto, ele avalia primeiro as localizações de prefixos, permitindo que o administrador altere essa tendência especificando localizações usando os modificadores = e ^~.

      Também é importante notar que, embora as localizações de prefixo geralmente selecionarem com base na correspondência mais longa e específica, a avaliação de expressões regulares é interrompida quando a primeira localização correspondente é encontrada. Isso significa que o posicionamento dentro da configuração tem vastas implicações para as localizações de expressões regulares.

      Por fim, é importante entender que as correspondências por expressões regulares dentro da correspondência de prefixo mais longa “passam à frente” quando o Nginx avalia as localizações de regex. Elas serão avaliadas, por ordem, antes de qualquer uma das outras correspondências por expressão regular serem consideradas. Maxim Dounin, um desenvolvedor do Nginx incrivelmente prestativo, explica nesta postagem essa parte do algoritmo de seleção.

      Quando a avaliação de blocos de localização salta para outras localizações?

      De maneira geral, quando um bloco de localização é selecionado para atender a uma solicitação, a solicitação é manuseada inteiramente dentro desse contexto a partir daquele ponto. Apenas a localização selecionada e as diretivas herdadas determinam como a solicitação é processada, sem a interferência de blocos de localização irmãos.

      Embora essa seja uma regra geral que permite projetar seus blocos de localização de maneira previsível, é importante perceber que há momentos em que uma nova procura de localização é acionada por determinadas diretivas dentro da localização selecionada. As exceções à regra de “apenas um bloco de localização” podem ter implicações sobre como a solicitação é realmente atendida e podem não se alinhar com as expectativas que você tinha quando projetou seus blocos de localização.

      Algumas diretivas que podem levar a esse tipo de redirecionamento interno são:

      • index
      • try_files
      • rewrite
      • error_page

      Vamos analisa-los brevemente.

      A diretiva index sempre leva a um redirecionamento interno se for usada para processar a solicitação. As correspondências exatas de localização são frequentemente usadas para acelerar o processo de seleção por finalizarem imediatamente a execução do algoritmo. No entanto, se você fizer uma correspondência exata de localização que é um diretório, existe uma boa chance de a solicitação ser redirecionada para uma localização diferente para ser de fato processada.

      Neste exemplo, a primeira localização corresponde a um URI de solicitação /exact, mas, para manusear a solicitação, a diretiva index herdada pelo bloco inicia um redirecionamento interno para o segundo bloco:

      index index.html;
      
      location = /exact {
      
          . . .
      
      }
      
      location / {
      
          . . .
      
      }
      

      No caso acima, se fosse realmente necessário que a execução permanecesse no primeiro bloco, você precisaria escolher um método diferente para satisfazer a solicitação para o diretório. Por exemplo, você poderia definir um index inválido para esse bloco e ativar o autoindex:

      location = /exact {
          index nothing_will_match;
          autoindex on;
      }
      
      location  / {
      
          . . .
      
      }
      

      Essa é uma maneira de impedir que o index mude os contextos, mas provavelmente não é útil para a maioria das configurações. Geralmente, uma correspondência exata nos diretórios pode ser útil para coisas como reescrever a solicitação (que também resulta em uma nova pesquisa de localização).

      Outra situação onde a localização de processamento pode ser reavaliada é com a diretiva try_files. Essa diretiva diz ao Nginx para verificar a existência de um conjunto de arquivos ou diretórios nomeados. O último parâmetro pode ser um URI para o qual o Nginx fará um redirecionamento interno.

      Considere a configuração a seguir:

      root /var/www/main;
      location / {
          try_files $uri $uri.html $uri/ /fallback/index.html;
      }
      
      location /fallback {
          root /var/www/another;
      }
      

      No exemplo acima, se uma solicitação for feita para /blahblah, a primeira localização inicialmente receberá a solicitação. Ele tentará encontrar um arquivo chamado blahblah no diretório /var/www/main. Se ele não puder encontrar um, ele seguirá procurando por um arquivo chamado blahblah.html. Em seguida, ele tentará ver se há um diretório chamado blahblah/ dentro do diretório /var/www/main. Se todas essas tentativas falharem, ele redirecionará para /fallback/index.html. Isso desencadeará outra pesquisa de localização que será capturada pelo segundo bloco de localização. Isso atenderá o arquivo /var/www/another/fallback/index.html.

      Outra diretiva que pode levar a uma mudança de bloco de localização é a rewrite. Quando se usa o último parâmetro com a diretiva rewrite, ou quando não se usa nenhum parâmetro, o Nginx procurará uma nova localização correspondente com base nos resultados de rewrite.

      Por exemplo, se modificarmos o último exemplo para incluir uma rewrite, é possível ver que a solicitação às vezes é passada diretamente para a segunda localização sem depender da diretiva try_files:

      root /var/www/main;
      location / {
          rewrite ^/rewriteme/(.*)$ /$1 last;
          try_files $uri $uri.html $uri/ /fallback/index.html;
      }
      
      location /fallback {
          root /var/www/another;
      }
      

      No exemplo acima, uma solicitação para /rewriteme/hello será manuseada inicialmente por um primeiro bloco de localização. Ela será reescrita para /hello e uma localização será pesquisada. Neste caso, ela corresponderá novamente à primeira localização e será processada pelo try_files como de costume, talvez retornando para /fallback/index.html se nada for encontrado (usando o redirecionamento interno do try_files que discutimos acima).

      No entanto, se uma solicitação for feita para /rewriteme/fallback/hello, o primeiro bloco novamente corresponderá. A diretiva rewrite é aplicada novamente, dessa vez resultando em /fallback/hello. A solicitação será então atendida a partir do segundo bloco de localização.

      Uma situação relacionada acontece com a diretiva return ao enviar os códigos de status 301 ou 302. Neste caso, a diferença é que isso resulta em uma solicitação inteiramente nova na forma de um redirecionamento visível externamente. Essa mesma situação pode ocorrer com a diretiva rewrite ao usar os sinalizadores redirect ou permanent. No entanto, essas pesquisas de localização não devem ser inesperadas, uma vez que os redirecionamentos visíveis externamente sempre resultam em uma nova solicitação.

      A diretiva error_page pode levar a um redirecionamento interno parecido com aquele criado por try_files. Essa diretiva é usada para definir o que deve acontecer quando certos códigos de status são encontrados. Isso provavelmente nunca será executado se o try_files estiver definido, já que essa diretiva manuseia todo o ciclo de vida de uma solicitação.

      Considere este exemplo:

      root /var/www/main;
      
      location / {
          error_page 404 /another/whoops.html;
      }
      
      location /another {
          root /var/www;
      }
      

      Todas as solicitações (que não sejam aquelas que começam com /another) serão manuseadas pelo primeiro bloco, que atenderá arquivos em /var/www/main. No entanto, se um arquivo não for encontrado (um status 404), um redirecionamento interno para /another/whoops.html ocorrerá, levando a uma nova pesquisa de localização que eventualmente chegará no segundo bloco. Este arquivo será atendido por /var/www/another/whoops.html.

      Como se vê, entender as circunstâncias nas quais o Nginx aciona uma nova pesquisa de localização pode ajudar a prever o comportamento que você verá ao fazer solicitações.

      Conclusão

      Compreender as maneiras como o Nginx processa as solicitações de clientes pode tornar seu trabalho como um administrador muito mais fácil. Você será capaz de saber qual bloco de servidor o Nginx selecionará com base em cada solicitação de cliente. Você também será capaz de dizer como o bloco de localização será selecionado com base no URI de solicitação. De maneira geral, saber como o Nginx seleciona diferentes blocos lhe dará a capacidade de rastrear os contextos que o Nginx aplicará para atender a cada solicitação.



      Source link