DevSecOps

24 dez, 2013

Lidando com o 404 nos servidores Web – #Melhores2013

Publicidade

O código de status HTTP 404 é bem famoso na Internet, ele diz que a requisição do cliente não foi encontrada no servidor. Muitas vezes, os usuários tentam acessar páginas que não existem dentro de um site: ou por causa de um link quebrado, ou uma digitação errada, ou algum outro erro humano. Para esses usuários, a página 404 pode se tornar uma porta de entrada para outros conteúdos dentro do site, ou até mesmo para apresentar alguma brincadeira e melhorar a imagem do site (acredite, adoramos brincadeiras com o 404).

Existem diversas formas de se lidar com o famoso “404 Página Não Encontrada”. Veja algumas delas:

  • Usar como página de entrada também: além de mostrar a mensagem de página não encontrada, o usuário pode ter uma lista dos artigos mais recentes, ou algum destaque;
  • Apresentar uma mensagem de não encontrado e um campo de busca para o usuário tentar encontrar o que está buscando realmente;
  • Já direcionar o usuário para os resultados de uma busca com os termos que ele tentou encontrar (baseado na URL);
  • Redirecionar para outro conteúdo;
  • Ou simplesmente mostrar uma página de não encontrado bonitinha e só.

Mas como fazer esse tipo de manuseio do 404? Geralmente, existem duas formas:

  1. Arquivo estático: quando o servidor não encontra a requisição, mostra para o usuário uma página HTML estática do jeito que você quiser. Essa página é simples de criar e pode ser usada para: redirecionar a requisição para outro lugar ou apresentar apenas a página de não encontrado;
  2. Resposta dinâmica: usada principalmente quando você quer obter informações da requisição (por exemplo: qual a URL que o usuário tentou acessar?) e mostrar a página de acordo. Com a resposta dinâmica, você consegue personalizar totalmente o 404.

Veremos aqui algumas técnicas de como fazer isso.

Nota: neste artigo, estou usando como exemplos um servidor Linux e os servidores web Apache e Nginx, que são os mais comuns que a gente encontra por aí (em minha experiência). As técnicas descritas aqui são genéricas e podem ser usadas em outros servidores web, mas as configurações com certeza mudarão de um tipo para outro 😉

Apenas um arquivo estático, nada mais!

Optar por simplicidade às vezes é bom! Economiza recursos da máquina e deixa tudo mais fácil de fazer e resolver.

Apache

No Apache, a diretriz que especifica o erro 404 é a seguinte:

ErrorDocument 404 /404.html

Ou seja, o usuário, quando requisitar uma página inexistente, vai ser servido com o conteúdo do arquivo /404.html. Esse arquivo 404.html fica na raiz do mesmo domínio que o usuário requisitou.

Exemplo, se o usuário requisitar a URL:

http://www.devin.com.br/naopenso-logo-naoexisto

No navegador, a URL vai continuar sendo essa, mas o conteúdo do arquivo na verdade vai ser o mesmo que se se o usuário acessasse:

http://www.devin.com.br/404.html

E o código de retorno será o HTTP 404, como vemos aqui:

$ wget -O /dev/null -S http://www.devin.com.br/naopenso-logo-naoexisto
[...]
HTTP request sent, awaiting response... 
HTTP/1.1 404 Not Found
[...]

Ainda, se você não quiser usar nenhuma página, opte por apenas gerar uma mensagem, usando a diretriz ErrorDocument da seguinte forma:

ErrorDocument 404 "A página não está aqui, foi comprar doces."

Experimente usar essas configurações no Apache e veja as diferenças. Você pode optar também por usá-las no arquivo .htaccess.

Nota: você pode usar uma URL externa para retornar o 404. Por exemplo:

ErrorDocument http://www.google.com.br/404

Mas, nesse caso, o usuário vai receber primeiro um redirect (status 302, ou redirecionamento temporário), para depois ler a página 404 no endereço informado. E, nesse caso, a URL no navegador vai mudar também. Na prática, o usuário nunca receberá um HTTP Status 404.

Nginx

No Nginx, usamos a seguinte linha para definir o 404:

error_page 404 /404.html;

Assim como no Apache, o usuário vai receber o conteúdo do arquivo 404.html da raiz do servidor, mas a URL no navegador vai continuar a mesma.

Você também pode usar uma URL externa:

error_page 404 http://www.google.com.br/404

Nesse caso, o comportamento vai ser o mesmo do Apache também: o usuário recebe um HTTP 302, para depois cair na página 404, o que vai fazer a URL do navegador mudar também.

Atenção! Limite mínimo de bytes!

Algumas versões mais novas de navegadores (exemplos: Internet Explorer e Chrome) vão verificar o tamanho da resposta do 404. Se o tamanho da resposta for MENOR ou IGUAL a 512 bytes, o navegador vai mostrar uma página personalizada do próprio navegador, em vez do HTML que você montou.

Nesse caso, certifique-se de que a página de 404 tenha mais que 512 bytes.

Um arquivo estático, mas com redirect

Algo comum pra muita gente é querer que o servidor redirecione a página para algum conteúdo externo ou seção do site. A primeira coisa em que a gente geralmente pensa é usar uma URL externa no ErrorDocument do Apache (ou error_page do Nginx).

Mas, como dito anteriormente, isso faz com que o cliente receba um redirecionamento 302, e por isso fica mais difícil de identificar pelos logs os 404. Os mecanismos de busca e os softwares de estatísticas de sites também não vão identificar os 404 de forma correta.

A solução para esse caso é usar o próprio HTML a seu favor. Usar uma tag de meta-refresh no header HTML resolve o problema, como mostra o exemplo:

<html>
  <head>
    <meta http-equiv="refresh" content="0;url=http://www.devin.com.br/busca/" />
    <title>Devin - 404 Página Não Encontrada</title>
  </head>
  <body>

  <br/>
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  &nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  &nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  <br/>

  </body>
</html>

Explicando: na terceira linha, temos tag “meta-refresh”. Ela faz com que o usuário, ao carregar essa página, seja imediatamente redirecionado para o endereço “http://www.devin.com.br/busca/”. Seria um jeito de redirecionar o usuário para uma página de busca dinâmica.

Na tag meta-reresh, o “content” nos diz em quantos segundos o usuário será redirecionado (zero é imediatamente, que é o nosso caso). E o “url” nos diz para onde o usuário será redirecionado.

Mas, cara! Pra que tanto “lixo” aí no corpo do HTML? Pra que tanto espaço (&ampnbsp;)?

Na seção anterior, eu disse que são necessários pelo menos 512 bytes na página para alguns navegadores mostrarem a página de fato. Esses espaços são apenas para ocupar os 512 bytes 😉

Um arquivo dinâmico, com SSI

O SSI (Server Side Includes) é um recurso que permite a páginas HTML comuns terem um pouco de dados dinâmicos. Não é tão completo como as linguagens dinâmicas para web a que estamos acostumados (PHP, Python, Ruby), mas no nosso caso serve muito bem.

A vantagem do SSI é que ele geralmente já vem com os servidores web.

Apache

Para usar o 404 dinâmico no Apache, primeiro você tem que habilitar o SSI e fazer uma página com algumas funcionalidades do SSI.

Por exemplo, vamos colocar uma página na URL “/error/404.html” do servidor. As configurações do Apache ficariam assim:

ErrorDocument 404 /error/404.html

<Location /error>
  Options +Includes
  AddOutputFilter INCLUDES .html
</Location>

Ou seja, eu indiquei que a página de erro 404 é a “/error/404.html” e o diretório “/error” no servidor tem o SSI habilitado, inclusive na extensão .html. Você vai ver muito por aí o SSI ser utilizado com extensão .shtml, mas realmente tanto faz, fica ao seu gosto.

Agora, a página que vai ficar no “/error/404.html”:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Oops! 404 Página Não Encontrada!</title>
  </head>
  <body>

  <h1>Oh não! 404 Página Não Encontrada!</h1>
  <p>
    O que você tentou encontrar não está aqui. Talvez a página tenha ido
    comprar uns doces ali na esquina. Veja pelo lado bom, até que pode
    ser uma coisa boa!
  </p>

  <h2>Detalhes:</h2>

  <ul>
    <li>URI que você tentou: <!--#echo var="REQUEST_URI" --></li>
    <li>Data atual: <!--#echo var="DATE_LOCAL" --></li>
  </ul>

  <h2>Todas as variáveis de ambiente</h2>

  <!--#printenv -->

  </body>
</html>

O que podemos notar nessa página são os “comandos” que começam com “<!–#”, que são os comandos do SSI. Nessa nossa página simples, mostramos qual endereço o usuário tentou buscar e a data. De quebra, aproveitei para listar todas as variáveis de ambiente que podem ser usadas, através do comando “printenv”.

Faça as configurações e teste no seu navegador agora mesmo! Acesse uma página que não existe.

Nginx

No nginx, o processo é bem parecido: habilitamos o SSI e definimos a nossa página de 404.

Primeiro, na configuração do nginx, habilitando o SSI:

location /error {
  ssi on;
}

error_page 404 /error/404.html;

Para a página de erro 404, você pode usar o mesmo exemplo que usamos no Apache.

Aviso: o comando <!–#printenv –> NÃO está disponível no nginx, mas as variáveis de ambiente geralmente são as mesmas.

Caso de uso

Quer um exemplo prático e legal para usar o SSI e aproveitar o seu dinamismo simples?

No primeiro exemplo de redirect, usamos um meta-refresh que redirecionava para uma página de busca. Que tal se a gente redirecionasse para a busca, passando como parâmetro a URI que o usuário digitou?

Assim, se ele acessou o ‘/doces’ e deu 404, ele pode ser redirecionado para uma busca por doces no site. Algo assim:

<meta http-equiv="refresh" content="0;url=https://www.google.com.br/?q=<!--#echo var="REQUEST_URI" -->" />

Hehehe! Esse exemplo vai pra página de busca do google já com o ‘/doces’ que o usuário acessou e deu 404. Você pode enviar isso como parâmetro para qualquer sistema que você tenha.

Um arquivo dinâmico, na sua linguagem favorita

Você pode usar uma página dinâmica de 404 com sua linguagem favorita. Pode ser realmente qualquer uma que o servidor suportar, você quem manda. PHP, Python, Ruby, Perl, Shell-Script (com CGI), você quem sabe.

Para isso, basta apenas apontar a página de erro para o seu script.

Por exemplo, para um PHP:

# No Apache
ErrorDocument 404 /error/404.php

# No nginx
error_page 404 /error/404.php;

Considerando que seu servidor já está configurado para responder com PHP para arquivos com extensão .php, agora você pode programar o que quiser, na linguagem dinâmica, utilizando todas as variáveis de ambiente.

Alguns exemplos de uso:

  • Já retornar um resultado de busca pela URL que o usuário acessou;
  • Gerar qualquer conteúdo dinâmico na página, como os últimos artigos postados no site, ou os mais populares no momento;
  • Mudar o código HTTP de 404 para qualquer outro (mexendo no cabeçalho HTTP);
  • Realmente, é só usar a imaginação;
  • NÃO ESQUECER DE PENSAR NO DESEMPENHO DA PÁGINA DINÂMICA.

Referências e Agradecimentos

Outras páginas com esses assuntos:

Obrigado por Tobias Sette Ferreira por ter me dado a ideia do artigo enquanto me perguntava sobre algo relacionado a este assunto.