Inscreva-se em minha Newsletter
Neste artigo falarei sobre criar regras de reescrita (Rewrite Rules) do NGINX. As regras alteram parte ou toda a URL em uma requisição do cliente, geralmente para um desses propósitos:
- Para informar aos usuários que o recurso que eles estão solicitando agora reside em um local diferente. Os exemplos de casos de uso são quando o nome de domínio do seu site mudou, quando você deseja que os clientes usem um formato de URL canônico (com ou sem o prefixo
www) e quando você, por qualquer motivo, precisa excluir uma URL. - Para controlar o fluxo de processamento no NGINX, por exemplo, para encaminhar solicitações a um servidor de aplicativos quando o conteúdo precisa ser gerado dinamicamente. A diretiva
try_filesé frequentemente usada para este propósito.
Observação: Saiba mais sobre como converter regras de reescrita do servidor Apache HTTP em regras de reescrita NGINX.
Irei assumir que você esteja familiarizado com os códigos de resposta HTTP e com as expressões regulares (NGINX usa a sintaxe Perl).
Comparando as diretivas return, rewrite e try_files
As duas diretivas para reescrever NGINX de uso geral são return e rewrite, e a diretiva try_files é uma maneira prática de direcionar solicitações aos servidores de aplicação. Vamos revisar o que essas diretivas fazem e como elas diferem.
return
A diretiva return é a mais simples das duas diretivas de uso geral e, por esse motivo, recomendamos usá-la em vez de reescrever quando possível (mais tarde sobre o porquê e quando). Você usa o return em um servidor (ou localmente) para especificar quais URLs serão regravados e define uma URL corrigida (regravado) para o cliente usar em solicitações futuras.
Aqui está um exemplo muito simples que redireciona os usuários para um novo nome de domínio:
server {
listen 80;
listen 443 ssl;
server_name www.old-name.com;
return 301 $scheme://www.new-name.com$request_uri;
}
A diretiva listen significa que o server se aplica ao tráfego HTTP e HTTPS. A diretiva server_name corresponde a URL de solicitação que têm o nome de domínio www.old‑name.com. A diretiva return informa ao NGINX para parar de processar a solicitação e enviar imediatamente o código 301 (movido permanentemente) e a URL reescrita para o cliente. A URL reescrita usa duas variáveis NGINX para capturar e replicar valores da URL de solicitação original: $scheme é o protocolo (http ou https) e $request_uri é o URI completo incluindo argumentos.
Para um código 3xx, o parâmetro url define a nova URL (reescrita).
return (301 | 302 | 303 | 307) url;
Para outros códigos, você pode definir opcionalmente uma string de texto que aparece no corpo da resposta (o texto padrão para o código HTTP, como Not Found para um 404, ainda está incluído no cabeçalho). O texto pode conter variáveis NGINX.
return (1xx | 2xx | 4xx | 5xx) ["texto"];
Por exemplo, esta diretiva pode ser apropriada para rejeitar solicitações que não têm um token de autenticação válido:
return 401 "Acesso negado porque o token expirou ou é inválido";
Existem também alguns atalhos que você pode usar, como omitir o código se ele for 302.
(Em alguns casos, você pode querer retornar uma resposta que é mais complexa do que você pode alcançar em uma string de texto. Com a diretiva error_page, você pode retornar uma página HTML personalizada completa para cada código HTTP, bem como alterar o código de resposta ou realizar um redirecionamento.)
Portanto, a diretiva return é simples de usar e adequada quando o redirecionamento atende a duas condições: a URL reescrita é apropriada para cada solicitação que corresponda ao servidor, e quando você pode construir a URL reescrita com variáveis NGINX padrão.
rewrite
Mas e se você precisar especificar URLs mais complexas, capturar elementos na URL que não existem nas variáveis NGINX ou alterar /adicionar elementos no path? Você pode usar a diretiva rewrite em tais casos.
Como a diretiva return, você inclui a diretiva rewrite em um server ou location que define as URLs a serem reescritas. Caso contrário, as duas diretivas são mais diferentes do que semelhantes, e a diretiva rewrite pode ser mais complicada de usar corretamente. Sua sintaxe é bastante simples:
rewrite regex URL [flag];
No primeiro argumento, regex, significa que o NGINX reescreve a URL apenas se ela corresponder à expressão regular especificada (além de corresponder a diretiva server ou location). O que significa que o NGINX deve processar mais.
Uma segunda diferença é que a diretiva rewrite pode retornar apenas o código 301 ou 302. Para retornar outros códigos, você precisa incluir uma diretiva return após a diretiva rewrite (veja o exemplo abaixo).
E, finalmente, a diretiva rewrite não necessariamente interrompe o processamento da solicitação do NGINX como a diretiva return, e não necessariamente envia um redirecionamento para o cliente. A menos que você indique explicitamente (com flags a sintaxe da URL) que deseja que o NGINX interrompa o processamento ou envie um redirecionamento, ele percorre toda a configuração procurando as diretivas definidas no módulo Rewrite (break, if, return, rewrite, e set), e os processa em ordem. Se uma URL reescrita corresponder a uma diretiva subsequente do módulo Rewrite, o NGINX executa a ação indicada na URL reescrita (geralmente reescrevendo-o novamente).
É aqui que as coisas podem ficar complicadas e você precisa planejar cuidadosamente como ordenar as diretivas para obter o resultado desejado. Por exemplo, se a location original e as regras de reescrita NGINX nele corresponderem ao URL reescrito, o NGINX pode entrar em um loop, aplicando a reescrita repetidamente até o limite interno de 10 vezes. Para saber todos os detalhes, consulte a documentação do módulo Rewrite. Conforme observado anteriormente, recomendo que, sempre que possível, você use a diretiva return.
Aqui está um exemplo de rewrite no NGINX. Ele corresponde a URLs que começam com a string /download e, em seguida, incluem o diretório /media/ ou /audio/ em algum lugar posteriormente no caminho. Ele substitui esses elementos por /mp3/ e adiciona a extensão de arquivo apropriada, .mp3 ou .ra. As variáveis $1 e $2 capturam os elementos que não estão mudando. Por exemplo, /download/cdn-west/media/file1 se torna /download/cdn-west/mp3/file1.mp3. Se houver uma extensão no nome do arquivo (como .flv), a expressão remove e a substitui por .mp3.
server {
# ...
rewrite ^(/download/.*)/media/(\w+)\.?.*$ $1/mp3/$2.mp3 last;
rewrite ^(/download/.*)/audio/(\w+)\.?.*$ $1/mp3/$2.ra last;
return 403;
# ...
}
Mencionamos acima que você pode adicionar sinalizadores a uma reescrita para controlar o fluxo de processamento. O último sinalizador no exemplo é um deles: ele diz ao NGINX para pular todas as diretivas do módulo Rewrite subsequentes no server ou location e iniciar uma busca por um novo location que corresponda a URL reescrita.
A diretiva return neste exemplo significa que se a URL não corresponder a nenhuma das diretivas rewrite, o código 403 será retornado ao cliente.
try_files
Assim como as diretivas return e rewrite, a diretiva try_files é colocada em um server ou location. Como parâmetros, leva uma lista de um ou mais arquivos e diretórios e uma URI final:
try_files file ... uri;
O NGINX verifica a existência dos arquivos e diretórios em ordem e fornece o primeiro que encontrar. Para indicar um diretório, adicione uma barra no final do nome do elemento. Se nenhum dos arquivos ou diretórios existir, NGINX executa um redirecionamento interno para a URI definido pelo elemento final uri.
Para que a diretiva try_files funcione, você também precisa definir a location que capture o redirecionamento interno, conforme mostrado no exemplo a seguir. O elemento final pode ser um location com nome, indicado por uma arroba inicial @.
A diretiva try_files normalmente usa a variável $uri, que representa a parte da URL após o nome do domínio.
No exemplo a seguir, o NGINX fornece um arquivo GIF padrão se o arquivo solicitado pelo cliente não existir. Quando o cliente solicita (por exemplo) http://www.domain.com/images/image1.gif, o NGINX primeiro procura image1.gif no diretório local especificado pela diretiva root ou alias que se aplica ao local (não mostrado no snippet). Se image1.gif não existir, NGINX procura por image1.gif/ e, se não existir, redireciona para/images/default.gif. Esse valor corresponde exatamente à segunda diretiva location, portanto, o processamento é interrompido e o NGINX entrega esse arquivo e o marca para ser armazenado em cache por 30 segundos.
location /images/ {
try_files $uri $uri/ /images/default.gif;
}
location = /images/default.gif {
expires 30s;
}
Exemplos de redirecionamentos no NGINX
Padronizando o Nome de Domínio
Um dos usos mais comuns das regras de reescrita do NGINX é capturar versões obsoletas ou não padrão do nome de domínio de um site e redirecioná-las para o nome atual. Existem vários casos de uso relacionados.
Redirecionando de um nome anterior para o nome atual
Este exemplo de regra de reescrita NGINX redireciona permanentemente as solicitações de www.old‑name.com e old‑name.com para www.new‑name.com, usando duas variáveis NGINX para capturar valores do URL da solicitação original - $scheme é o protocolo original (http ou https) e $request_uri é o URI completo (após o nome do domínio), incluindo os argumentos:
server {
listen 80;
listen 443 ssl;
server_name www.old-name.com old-name.com;
return 301 $scheme://www.new-name.com$request_uri;
}
Como $request_uri captura a parte da URL que segue o nome de domínio, essa reescrita é adequada se houver uma correspondência igual de páginas entre os sites antigo e novo (por exemplo, www.novo-nome.com/sobre tem o mesmo conteúdo de www.nome.com/sobre). Se você reorganizou o site além de alterar o nome de domínio, pode ser mais seguro redirecionar todas as solicitações para a página inicial, omitindo $request_uri:
server {
listen 80;
listen 443 ssl;
server_name www.old-name.com old-name.com;
return 301 $scheme://www.new-name.com;
}
Alguns sites usam a diretiva rewrite para esses casos:
# NÃO RECOMENDADO
rewrite ^ $scheme://www.new-name.com$request_uri perman
Isso é menos eficiente do que a diretiva return equivalente, pois para o NGINX processar uma expressão regular, embora simples (o caractere ^, que corresponde a URL original). A diretiva return correspondente também é mais fácil para um leitor humano interpretar: o return 301 indica mais claramente que o NGINX retorna o código 301 do que o rewrite ... permanent.
Adicionando e removendo o prefixo www
Estes exemplos adicionam e removem o prefixo www:
# add 'www'
server {
listen 80;
listen 443 ssl;
server_name domain.com;
return 301 $scheme://www.domain.com$request_uri;
}
# remove 'www'
server {
listen 80;
listen 443 ssl;
server_name www.domain.com;
return 301 $scheme://domain.com$request_uri;
}
Novamente, o return é preferível ao rewrite equivalente. O rewrite requer a interpretação de uma expressão regular ^(.*)$e a criação de uma variável personalizada $1 que na verdade é equivalente à variável $request_uri embutida.
# NÃO RECOMENDADO
rewrite ^(.*)$ $scheme://www.domain.com$1 permanent;
Redirecionando todo o tráfego para um domínio
Este é um caso especial em que redirecionamos todo o tráfego para a página inicial do site quando o URL de solicitação não corresponde a nenhum server ou location, talvez porque o nome de domínio esteja incorreto. Ele funciona combinando o parâmetro default_server com a diretiva listen e o exemplo.com como o parâmetro para a diretiva server_name.
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name exemplo.com;
return 301 $scheme://www.newdomain.com;
}
As solicitações que não correspondem a nenhum outro server na configuração acabam aqui, porém, o parâmetro default_server diz ao NGINX para usar este bloco para. Ao omitir a variável $request_uri da URL reescrita, redirecionamos todas as solicitações para a página inicial, isso é uma boa ideia porque as solicitações com o nome de domínio errado são particularmente propensas a usar URIs que não existem no site.
Forçando todas as requisições para usar SSL / TLS
Este server força todos os visitantes a usarem SSL / TLS.
server {
listen 80;
server_name www.domain.com;
return 301 https://www.domain.com$request_uri;
}
Alguns sites usam uma condição if e a diretiva rewrite para este caso, veja:
# NÃO RECOMENDADO
if ($scheme != "https") {
rewrite ^ https://www.mydomain.com$uri permanent;
}
Mas esse método requer processamento extra porque NGINX deve avaliar a condição if e processar a expressão regular na diretiva rewrite.
Habilitando Pretty Permalinks para sites WordPress
O NGINX é uma plataforma muito popular para sites que usam WordPress. A seguinte diretiva try_files diz ao NGINX para verificar a existência de um arquivo, $uri, e então do diretório, $uri/. Se nem o arquivo ou diretório existir, o NGINX retorna um redirecionamento para /index.php e passa os argumentos da query string, que são capturados pelo parâmetro $args.
location / {
try_files $uri $uri/ /index.php?$args;
}
Rejeitando solicitações de extensões de arquivo não suportadas
Por vários motivos, seu site pode receber URLs de solicitação que terminam em uma extensão de arquivo correspondente a um servidor de aplicativo que você não está executando. Neste exemplo o servidor de aplicativos é Ruby on Rails, portanto, as solicitações com tipos de arquivo manipulados por outros servidores de aplicativos (Active Server Pages, PHP, CGI e assim por diante) não podem ser atendidas e precisam ser rejeitadas. Em um server que passa todas as solicitações geradas dinamicamente para o aplicativo, este location elimina as solicitações de tipos de arquivo não suportados pelo Rails antes de chegarem à fila.
location ~ .(aspx|php|jsp|cgi)$ {
return 410;
}
A rigor, o código de resposta 410 (Gone) se destina a situações em que o recurso solicitado costumava estar disponível na URL, mas não está mais, e o servidor não sabe sua localização atual, se houver. A vantagem sobre o código de resposta 404 é que ele indica explicitamente que o recurso está permanentemente indisponível, então os clientes não enviarão a solicitação novamente.
Você pode fornecer aos clientes uma indicação mais precisa do motivo da falha, retornando o código de resposta 403 (Forbidden) e uma explicação como “O servidor aceita apenas as solicitações Ruby” como string de texto. Como alternativa, a diretiva deny all retorna 403 sem uma explicação:
location ~ .(aspx|php|jsp|cgi)$ {
deny all;
}
O código 403 confirma implicitamente que o recurso solicitado existe, portanto, o código 404 pode ser a melhor escolha se você deseja obter “segurança”, fornecendo ao cliente o mínimo de informações possível. A desvantagem é que os clientes podem repetir a solicitação repetidamente porque 404 não indica se a falha é temporária ou permanente.
Configurando um redirecionamento personalizado
Neste exemplo do MODXCloud, você tem um recurso que funciona como um controlador para um conjunto de URLs. Seus usuários podem usar um nome mais legível para um recurso, e você reescreve (não é redirecionar) a URL para ser tratada pelo controlador em listing.html.
rewrite ^/listings/(.*)$ /listing.html?listing=$1 last;
Como exemplo, a URL amigável http://mysite.com/listings/123 é regravada em uma URL controlada pelo controlador listing.html, http://mysite.com/listing.html?listing=123.