Hackeando o Nginx: condicionais múltiplas

Ontem tirei o dia para otimizar o desempenho de meu blog, e obtive resultados deveras animadores, ao conseguir fazer com que o código dele fosse totalmente compatível com o HHVM.

Porém, descobri um problema que talvez estivesse impedindo algumas pessoas de, sob determinadas circunstâncias, visualizar corretamente as imagens dos posts.

Uso o Nginx para servir conteúdo via HTTP, e o plugin EWWW Image Optimizer (leia mais sobre ele aqui) para produzir versões mais leves das imagens que subo para cá. Este plugin permite converter imagens .jpg e .png comuns para o formato super otimizado .webp, que por enquanto é suportado apenas pelo Chrome, pelo Opera e pelos navegadores padrão dos dispositivos Android.

Eu tinha um código de detecção de habilidade do navegador para exibir imagens .webp (é fácil, o próprio navegador anuncia essa capacidade ao fazer uma requisição), caso uma imagen .webp com o mesmo nome da original existisse (que é precisamente o que o plugin faz).

Porém, em alguns casos a detecção parecia não estar funcionando, pois visitantes usando o Firefox eram solicitados a fazer download da imagem .webp, e visitantes usando o Chrome visualizavam a imagem .jpg normal. Deduzi que o problema estava no cache, uma vez que a imagem .webp era servida com a extensão original.

A solução para o problema é simples: o Nginx deve garantir que duas condições sejam aceitas para só então servir a imagem .webp: ela tem que existir no servidor, e o visitante tem que ter um navegador capaz de exibir o .webp.

Entretanto, o Nginx não permite utilizar nenhum operador lógico and nas suas condicionais, tampouco permite estruturas if aninhadas. Isso poderia ser um problema para quem faz questão de resolver as coisas de maneira elegante. Para mim, apenas oportunidade de testar um conceito, e aplicar um hack no Nginx.

O código abaixo faz a mágica:

location ~* ^/.+\.(png|jpg|jpeg)$ {
  set $red Z;

  if ($http_accept ~* "webp")    {
    set $red A;
  }
  if (-f $request_filename.webp) {
    set $red "${red}B";
  }

  if ($red = "AB") {
      add_header Vary Accept;
      rewrite (.*) $1.webp redirect;
  }
}

O segredo está na variável $red (não sou muito criativo com nomes de variáveis). Ela é inicializada com um valor arbitrário “Z”.

Em seguida eu testo se o navegador aceita imagens .webp. Se sim, troco o valor de $red para “A”.

Depois verifico se a imagem .webp existe para ser servida ao visitante; caso exista, a variável $red é alterada para o valor dela mesma, mais um “B” ao final.

Nesse ponto, $red pode contar uma combinação de valores, mas o único que realmente importa é “AB” (“A” de que o navegador suporta o formato e “B” de que o arquivo existe para ser servido), caso em que é feito o redirecionamento da imagem original para a imagem .webp otimizada.

Justamente este redirecionamento que é transparente para o visitante é que impede os conflitos com o cache de que falei no início.

Newsletter

Gostou deste conteúdo? Informe seu email e receba gratuitamente todos os novos posts na sua caixa de entrada (será necessário confirmar a inscrição em seguida, verifique sua caixa postal).

Leia também
3 comentários
  • osgregs Responder

    gostei da solucao, se possivel exibir o restante do codigo excluindo dados sigilosos, agradeceria.

    • Janio Sarmento Responder

      Como assim “o restante do código?”

      O código acima está completo, e faz exatamente o que eu quero que faça. Do que foi que esqueci? 😀

      • osgregs Responder

        realmente… falha minha, nao tinha visto setado o valor Z

Responder osgregsMenu

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.