Evitar solicitações de rede desnecessárias com o cache HTTP

Buscar recursos pela rede é lento e caro:

  • Respostas grandes exigem muitas viagens de ida e volta entre o navegador e o servidor.
  • Sua página não será carregada até que todos os recursos críticos sejam totalmente baixados.
  • Se uma pessoa estiver acessando seu site com um plano de dados móveis limitado, cada solicitação de rede desnecessária será um desperdício de dinheiro.

Como evitar solicitações de rede desnecessárias? O cache HTTP do navegador é sua primeira linha de defesa. Não é necessariamente a abordagem mais eficiente ou flexível, e você tem controle limitado sobre o ciclo de vida das respostas armazenadas em cache, mas é eficaz, tem suporte em todos os navegadores e não exige muito trabalho.

Este guia mostra os conceitos básicos de uma implementação eficaz de armazenamento em cache HTTP.

Compatibilidade com navegadores

Na verdade, não existe uma única API chamada cache HTTP. É o nome geral de uma coleção de APIs de plataforma da Web. Essas APIs são compatíveis com todos os navegadores:

Cache-Control

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

ETag

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

Last-Modified

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

Como funciona o cache HTTP

Todas as solicitações HTTP feitas pelo navegador são roteadas primeiro para o cache do navegador para verificar se há uma resposta válida em cache que possa ser usada para atender à solicitação. Se houver uma correspondência, a resposta será lida no cache, o que elimina a latência de rede e os custos de dados gerados pela transferência.

O comportamento do cache HTTP é controlado por uma combinação de cabeçalhos da solicitação e cabeçalhos de resposta. Em um cenário ideal, você tem controle sobre o código do aplicativo da Web (que determina os cabeçalhos da solicitação) e a configuração do servidor da Web (que determina os cabeçalhos de resposta).

Consulte o artigo Armazenamento em cache HTTP da MDN para uma visão geral conceitual mais detalhada.

Cabeçalhos de solicitação: use os padrões (geralmente)

Há vários cabeçalhos importantes que precisam ser incluídos nas solicitações de saída do seu app da Web, mas o navegador quase sempre cuida da configuração deles em seu nome quando faz solicitações. Os cabeçalhos da solicitação que afetam a verificação de atualização, como If-None-Match e If-Modified-Since, aparecem com base no entendimento do navegador sobre os valores atuais no cache HTTP.

Isso é ótimo, porque significa que você pode continuar incluindo tags como <img src="my-image.png"> no HTML, e o navegador vai cuidar automaticamente do armazenamento em cache HTTP para você, sem esforço extra.

Cabeçalhos de resposta: configurar o servidor da Web

A parte mais importante da configuração do armazenamento em cache HTTP são os cabeçalhos que o servidor da Web adiciona a cada resposta enviada. Os cabeçalhos a seguir influenciam o comportamento de armazenamento em cache:

  • Cache-Control. O servidor pode retornar uma diretiva Cache-Control para especificar como e por quanto tempo o navegador e outros caches intermediários devem armazenar em cache a resposta individual.
  • ETag. Quando o navegador encontra uma resposta em cache expirada, ele pode enviar um pequeno token (geralmente um hash do conteúdo do arquivo) ao servidor para verificar se o arquivo foi alterado. Se o servidor retornar o mesmo token, o arquivo é o mesmo e não é necessário fazer o download novamente.
  • Last-Modified. Esse cabeçalho tem a mesma finalidade que ETag, mas usa uma estratégia baseada em tempo para determinar se um recurso foi alterado, ao contrário da estratégia baseada em conteúdo de ETag.

Alguns servidores da Web têm suporte integrado para definir esses cabeçalhos por padrão, enquanto outros os deixam de fora, a menos que você os configure explicitamente. Os detalhes específicos de como configurar cabeçalhos variam muito de acordo com o servidor da Web usado. Consulte a documentação do seu servidor para ver os detalhes mais precisos.

Para facilitar, confira as instruções de configuração de alguns servidores da Web conhecidos:

Deixar de fora o cabeçalho de resposta Cache-Control não desativa o armazenamento em cache HTTP. Em vez disso, os navegadores adivinham qual tipo de comportamento de armazenamento em cache faz mais sentido para um determinado tipo de conteúdo. É provável que você queira mais controle do que isso, então reserve um tempo para configurar os cabeçalhos de resposta.

Quais valores de cabeçalho de resposta você deve usar?

Há dois cenários importantes que você precisa considerar ao configurar os cabeçalhos de resposta do servidor da Web.

Armazenamento em cache de longa duração para URLs com controle de versões

Como os URLs com controle de versão podem ajudar sua estratégia de armazenamento em cache
Os URLs com controle de versão são uma boa prática porque facilitam a invalidação de respostas armazenadas em cache.

Suponha que seu servidor instrua os navegadores a armazenar em cache um arquivo CSS por um ano (Cache-Control: max-age=31536000), mas seu designer acabou de fazer uma atualização de emergência que você precisa implantar imediatamente. Como você notifica os navegadores para atualizar a cópia em cache "desatualizada" do arquivo? Não é possível, pelo menos não sem mudar o URL do recurso.

Depois que o navegador armazena a resposta em cache, a versão em cache é usada até que não seja mais recente, conforme determinado por max-age ou expires, ou até que seja removida do cache por algum outro motivo, por exemplo, o usuário limpando o cache do navegador. Como resultado, usuários diferentes podem acabar usando versões diferentes do arquivo quando a página é criada: os usuários que acabaram de buscar o recurso usam a nova versão, enquanto os usuários que armazenaram em cache uma cópia anterior (mas ainda válida) usam uma versão mais antiga da resposta.

Como ter o melhor dos dois mundos: armazenamento em cache do lado do cliente e atualizações rápidas? Você muda o URL do recurso e força o usuário a baixar a nova resposta sempre que o conteúdo dela muda. Normalmente, isso é feito incorporando uma impressão digital do arquivo ou um número de versão no nome do arquivo, por exemplo, style.x234dff.css.

Ao responder a solicitações de URLs que contêm "impressão digital" ou informações de controle de versões e cujo conteúdo nunca deve mudar, adicione Cache-Control: max-age=31536000 às suas respostas.

Definir esse valor informa ao navegador que,quando ele precisar carregar o mesmo URL em qualquer momento no próximo ano (31.536.000 segundos,o valor máximo aceito), ele poderá usar imediatamente o valor no cache HTTP, sem precisar fazer uma solicitação de rede para seu servidor da Web. Isso é ótimo: você ganhou imediatamente a confiabilidade e a velocidade que vêm de evitar a rede.

Ferramentas de build como o webpack podem automatizar o processo de atribuição de impressões digitais de hash aos URLs dos recursos.

Revalidação do servidor para URLs sem versão

Infelizmente, nem todos os URLs carregados têm controle de versão. Talvez não seja possível incluir uma etapa de build antes de implantar o app da Web, então não é possível adicionar hashes aos URLs dos recursos. Além disso, todo aplicativo da Web precisa de arquivos HTML, que (quase!) nunca vão incluir informações de controle de versões, já que ninguém vai se dar ao trabalho de usar o app da Web se precisar lembrar que o URL a ser acessado é https://example.com/index.34def12.html. Então, o que você pode fazer com esses URLs?

Nesse cenário, você precisa admitir a derrota. O armazenamento em cache HTTP por si só não é suficiente para evitar completamente a rede. Não se preocupe, você vai aprender sobre os service workers em breve, que vão dar o suporte necessário para virar o jogo a seu favor. No entanto, há algumas etapas que você pode seguir para garantir que as solicitações de rede sejam as mais rápidas e eficientes possíveis.

Os seguintes valores de Cache-Control podem ajudar você a ajustar onde e como os URLs sem versão são armazenados em cache:

  • no-cache. Isso instrui o navegador a revalidar com o servidor sempre antes de usar uma versão em cache do URL.
  • no-store. Isso instrui o navegador e outros caches intermediários (como CDNs) a nunca armazenar nenhuma versão do arquivo.
  • private. Os navegadores podem armazenar o arquivo em cache, mas os caches intermediários não.
  • public. A resposta pode ser armazenada por qualquer cache.

Consulte o Apêndice: fluxograma de Cache-Control para visualizar o processo de decisão sobre quais valores de Cache-Control usar. Cache-Control também pode aceitar uma lista de diretivas separadas por vírgulas. Consulte Apêndice: exemplos de Cache-Control.

Definir ETag ou Last-Modified também pode ajudar. Como mencionado em Cabeçalhos de resposta, ETag e Last-Modified têm a mesma finalidade: determinar se o navegador precisa fazer o download novamente de um arquivo em cache que expirou. Recomendamos o uso de ETag porque ele é mais preciso.

Exemplo de ETag

Suponha que 120 segundos se passaram desde a busca inicial e o navegador iniciou uma nova solicitação para o mesmo recurso. Primeiro, o navegador verifica o cache HTTP e encontra a resposta anterior. Infelizmente, o navegador não pode usar a resposta anterior porque ela expirou. Nesse momento, o navegador pode enviar uma nova solicitação e buscar a nova resposta completa. No entanto, isso é ineficiente porque, se o recurso não mudou, não há motivo para fazer o download das mesmas informações que já estão no cache.

Esse é o problema que os tokens de validação, conforme especificado no cabeçalho ETag, foram projetados para resolver. O servidor gera e retorna um token arbitrário, que normalmente é um hash ou outra impressão digital do conteúdo do arquivo. O navegador não precisa saber como a impressão digital é gerada, apenas enviá-la ao servidor na próxima solicitação. Se a impressão digital ainda for a mesma, o recurso não mudou, e o navegador pode pular o download.

Definir ETag ou Last-Modified torna a solicitação de revalidação muito mais eficiente, permitindo que ela acione os cabeçalhos da solicitação If-Modified-Since ou If-None-Match mencionados em Cabeçalhos de solicitação.

Quando um servidor da Web configurado corretamente vê esses cabeçalhos de solicitação recebidos, ele pode confirmar se a versão do recurso que o navegador já tem no cache HTTP corresponde à versão mais recente no servidor da Web. Se houver uma correspondência, o servidor poderá responder com uma resposta HTTP 304 Not Modified, que é o equivalente a "Ei, continue usando o que você já tem!" Há poucos dados para transferir ao enviar esse tipo de resposta. Por isso, geralmente é muito mais rápido do que enviar uma cópia do recurso solicitado.

Uma visualização de um cliente solicitando um recurso e o servidor respondendo com um cabeçalho 304.
O navegador solicita /file do servidor e inclui o cabeçalho If-None-Match para instruir o servidor a retornar o arquivo completo apenas se o ETag do arquivo no servidor não corresponder ao valor If-None-Match do navegador. Nesse caso, os dois valores correspondem, então o servidor retorna uma resposta 304 Not Modified com instruções sobre por quanto tempo o arquivo deve ser armazenado em cache (Cache-Control: max-age=120).

Resumo

O cache HTTP é uma maneira eficaz de melhorar o desempenho de carregamento porque reduz as solicitações de rede desnecessárias. Ele é compatível com todos os navegadores e não dá muito trabalho para configurar.

As seguintes configurações de Cache-Control são um bom começo:

  • Cache-Control: no-cache para recursos que precisam ser revalidados com o servidor antes de cada uso.
  • Cache-Control: no-store para recursos que nunca devem ser armazenados em cache.
  • Cache-Control: max-age=31536000 para recursos com controle de versão.

E o cabeçalho ETag ou Last-Modified pode ajudar você a revalidar recursos de cache expirados com mais eficiência.

Saiba mais

Se você quiser ir além do básico sobre o uso do cabeçalho Cache-Control, confira o guia de Jake Archibald sobre Práticas recomendadas de armazenamento em cache e problemas com max-age (em inglês).

Consulte Aproveite seu cache para saber como otimizar o uso do cache para visitantes recorrentes.

Apêndice: mais dicas

Se você tiver mais tempo, confira outras maneiras de otimizar o uso do cache HTTP:

  • Use URLs consistentes. Se você veicular o mesmo conteúdo em URLs diferentes, ele será buscado e armazenado várias vezes.
  • Minimizar a desistência de usuários. Se parte de um recurso (como um arquivo CSS) é atualizada com frequência, mas o restante do arquivo não (como o código da biblioteca), considere dividir o código atualizado com frequência em um arquivo separado e usar uma estratégia de armazenamento em cache de curta duração para o código atualizado com frequência e uma estratégia de armazenamento em cache de longa duração para o código que não muda com frequência.
  • Confira a nova diretiva stale-while-revalidate se algum grau de desatualização for aceitável na sua política Cache-Control.

Apêndice: fluxograma do Cache-Control

Fluxograma
O processo de decisão para definir os cabeçalhos Cache-Control.

Apêndice: exemplos de Cache-Control

Valor de Cache-Control Explicação
max-age=86400 A resposta pode ser armazenada em cache por navegadores e caches intermediários por até 1 dia (60 segundos x 60 minutos x 24 horas).
private, max-age=600 A resposta pode ser armazenada em cache pelo navegador (mas não por caches intermediários) por até 10 minutos (60 segundos x 10 minutos).
public, max-age=31536000 A resposta pode ser armazenada por qualquer cache por um ano.
no-store A resposta não pode ser armazenada em cache e precisa ser buscada na íntegra em cada solicitação.