Sobre o uso de regexp no mapa nginx

Faz muito tempo que não escrevo nada, então vamos diluir o final da sexta-feira com buscas simples, mas nem sempre óbvias, no Nginx .





Este servidor web tem uma diretiva de mapa maravilhosa que permite simplificar e encurtar as configurações. A essência da diretiva é que ela permite criar uma nova variável, cujo valor depende dos valores de uma ou mais das variáveis ​​originais. A diretiva se torna ainda mais poderosa ao usar expressões regulares, mas ao mesmo tempo é esquecida sobre um ponto importante. Trecho do manual:





Como as variáveis ​​são avaliadas apenas no momento do uso, mesmo um grande número de declarações de variáveis ​​de mapa em si não incorre em nenhuma sobrecarga de processamento de consulta adicional.





E aqui é importante não só que “o mapa não acarreta custos adicionais para o processamento de pedidos”, mas também que “as variáveis ​​são calculadas apenas no momento da utilização”.





Como você sabe, a configuração do Nginx é principalmente declarativa. Isso também se aplica à diretiva map e, apesar de estar localizada no contexto http , não é avaliada até que a solicitação seja processada. Ou seja, ao usar a variável resultante no servidor de contextos , localização, se , etc. nós "substituímos" não o resultado final do cálculo, mas apenas a "fórmula" pela qual esse resultado será calculado no momento certo. Não há problemas nesta casuística de configuração até usarmos expressões regulares. Nomeadamente expressões regulares com seleções. Mais precisamente, expressões regulares com seleções sem nome. É mais fácil mostrar com um exemplo.





Digamos que temos um domínio example.com com muitos subdomínios de terceiro nível , como ru.example.com, en.example.com, de.example.com , etc., e queremos redirecioná-los para novos subdomínios ru.example. org, en.example.org, de.example.org , etc. Em vez de descrever centenas de linhas de redirecionamentos, faremos o seguinte:





map $host $redirect_host {
  default "example.org";
  "~^(\S+)\.example\.com$"  $1.example.org;
}
server {
    listen       *:80;
    server_name  .example.com;
  location / {
        rewrite ^(.*)$ https://$redirect_host$1 permanent;
    
}
      
      



Aqui, esperávamos erroneamente que, ao solicitar ru.example.com, a regex fosse calculada no mapa e, portanto, quando chegar à localização , a variável $ redirect_host conteria o valor de ru.example.org , mas na realidade isso não é o caso:





$ GET -Sd ru.example.com
GET http://ru.example.com
301 Moved Permanently
GET https://ru.example.orgru
      
      



, ru.example.orgru. - , " " rewrite .





- regexp map , , :





map $host $redirect_host {
  default "example.org";
  "~^(\S+)\.example\.com$"  $1.example.org;
}
server {
    listen       *:80;
    server_name  .example.com;
    location / {
        return 301 https://$redirect_host$request_uri;
    }
}
      
      



, ( ).

map:





map $host $redirect_host {
  default "example.org";
  "~^(?<domainlevel3>\S+)\.example\.com$"  $domainlevel3.example.org;
}
server {
    listen       *:80;
    server_name  .example.com;
    location / {
        rewrite ^(.*)$ https://$redirect_host$1 permanent;
    }
}
      
      



:





$ GET -Sd ru.example.com
GET http://ru.example.com
301 Moved Permanently
GET https://ru.example.orgru
      
      



já que nossa alocação sem nome $ 1 obterá o resultado de $ domainlevel3 nomeado . Ou seja, você precisa usar seleções nomeadas em ambos os regexes:





map $host $redirect_host {
  default "example.org";
  "~^(?<domainlevel3>\S+)\.example\.com$"  $domainlevel3.example.org;
}
server {
    listen       *:80;
    server_name  .example.com;
    location / {
        rewrite ^(?<requri>.*)$ https://$redirect_host$requri permanent;
    }
}
      
      



E agora tudo funciona conforme o esperado:





$ GET -Sd ru.example.com
GET http://ru.example.com
301 Moved Permanently
GET https://ru.example.org/
      
      






All Articles