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

A busca de recursos na rede é lenta e cara:

  • Respostas grandes exigem muitas idas e voltas entre o navegador e o servidor.
  • Sua página não será carregada até que o download de todos os recursos críticos seja concluído.
  • Se uma pessoa estiver acessando seu site com um plano limitado de dados móveis, toda solicitação de rede desnecessária é um desperdício de dinheiro.

Como você pode evitar solicitações de rede desnecessárias? O cache HTTP do navegador é sua primeira linha de defesa. Não é necessariamente a abordagem mais poderosa ou flexível, e você tem controle limitado sobre o ciclo de vida das respostas armazenadas em cache, mas é eficaz, é compatível com 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

Não existe uma única API chamada Cache HTTP. É o nome geral de uma coleção de APIs de plataforma da Web. Estas APIs são compatíveis com todos os navegadores:

Cache-Control

Compatibilidade com navegadores

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

Origem

ETag

Compatibilidade com navegadores

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

Origem

Last-Modified

Compatibilidade com navegadores

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

Origem

Como funciona o cache HTTP

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

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

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

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

Existem diversos cabeçalhos importantes que devem ser incluídos nas solicitações de saída do seu aplicativo da web, mas o navegador quase sempre se encarrega de defini-los em seu nome quando faz as solicitações. Os cabeçalhos de solicitação que afetam a verificação de atualização, como If-None-Match e If-Modified-Since, aparecem com base na compreensão do navegador dos valores atuais no cache HTTP.

Essa é uma boa notícia. Isso significa que você pode continuar incluindo tags como <img src="my-image.png"> no seu HTML, e o navegador fará automaticamente o armazenamento em cache HTTP para você, sem esforço extra.

Cabeçalhos de resposta: configure seu servidor da Web

A parte da configuração do armazenamento em cache HTTP que mais importa são os cabeçalhos que seu servidor da Web adiciona a cada resposta de saída. Todos os cabeçalhos a seguir afetam o comportamento eficaz do 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 a resposta individual em cache.
  • ETag Quando o navegador encontra uma resposta em cache expirada, ele pode enviar um pequeno token (normalmente um hash do conteúdo do arquivo) para o servidor para verificar se o arquivo foi alterado. Se o servidor retornar o mesmo token, o arquivo será o mesmo e não será necessário fazer o download dele novamente.
  • Last-Modified Esse cabeçalho tem a mesma finalidade de ETag, mas usa uma estratégia baseada em tempo para determinar se um recurso mudou, em oposição à 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 totalmente de fora, a menos que você os configure explicitamente. Os detalhes específicos sobre como configurar cabeçalhos variam muito de acordo com o servidor da Web usado. Consulte a documentação do servidor para ver os detalhes mais precisos.

Para evitar um pouco de pesquisa, veja as instruções sobre como configurar alguns servidores da Web mais usados:

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

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

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

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

Suponha que seu servidor instrua os navegadores a armazenar em cache um arquivo CSS por um ano (Cache-Control: max-age=31536000), mas o designer acabou de fazer uma atualização de emergência que você precisa implantar imediatamente. Como notificar os navegadores sobre a atualização de "desatualizado?" cópia em cache do arquivo? Isso não é possível, pelo menos não sem alterar 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 atualizada, conforme determinado por max-age ou expires, ou até que seja removida do cache por algum outro motivo, por exemplo, a limpeza do cache do navegador pelo usuário. Como resultado, usuários diferentes podem acabar usando versões diferentes do arquivo quando a página é construída: os usuários que acabaram de buscar o recurso usam a nova versão, enquanto usuários que armazenaram em cache uma cópia anterior (mas ainda válida) usam uma versão mais antiga da resposta.

Como você obtém o melhor dos dois mundos: armazenamento em cache do lado do cliente e atualizações rápidas? Você altera o URL do recurso e força o usuário a fazer o download da nova resposta sempre que o conteúdo é alterado. 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 não possa ser alterado, adicione Cache-Control: max-age=31536000 às suas respostas.

A definição desse valor informa ao navegador que,quando ele precisar carregar o mesmo URL a qualquer momento no próximo ano (31.536.000 segundos,o valor máximo suportado), ele poderá usar imediatamente o valor no cache HTTP, sem ter que fazer uma solicitação de rede ao servidor da Web. Excelente. Você conquistou imediatamente a confiabilidade e a velocidade que vem de evitar a rede.

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

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

Infelizmente, nem todos os URLs carregados têm controle de versões. Talvez não seja possível incluir uma etapa de build antes de implantar seu app da Web. Por isso, não vai ser possível adicionar hashes aos URLs dos recursos. E todos os aplicativos da Web precisam de arquivos HTML. Esses arquivos (quase!) nunca incluem informações de versão, já que ninguém se preocupará em usar seu app da Web se precisar lembrar que o URL a ser acessado é https://example.com/index.34def12.html. O que você pode fazer com esses URLs?

Esse é um cenário em que você precisa admitir a derrota. O armazenamento em cache HTTP por si só não é eficiente o suficiente para evitar a rede completamente. (Não se preocupe, em breve você aprenderá sobre os service workers, que fornecem o suporte necessário para retornar a batalha a seu favor. Mas você pode seguir algumas etapas para garantir que as solicitações de rede sejam as mais rápidas e eficientes possíveis.

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

  • no-cache: Isso instrui o navegador que ele deve revalidar com o servidor todas as vezes 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 caches intermediários não.
  • public: A resposta pode ser armazenada por qualquer cache.

Consulte o Apêndice: fluxograma de Cache-Control para conferir o processo para decidir quais valores de Cache-Control usar. Cache-Control também pode aceitar uma lista de diretivas separadas por vírgulas. Consulte o Apêndice: exemplos do 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 armazenado em cache que expirou. Recomendamos usar ETag por ser mais preciso.

Suponha que se passaram 120 segundos desde a busca inicial e que o navegador iniciou uma nova solicitação para o mesmo recurso. Primeiro, o navegador verifica o cache HTTP e encontra a resposta anterior. O navegador não pode usar a resposta anterior porque ela expirou. Nesse ponto, o navegador poderá despachar uma nova solicitação e buscar a nova resposta completa. No entanto, isso é ineficiente, porque se o recurso não foi alterado, 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. ele só precisa enviá-lo ao servidor na próxima solicitação. Se a impressão digital ainda for a mesma, isso significa que o recurso não foi alterado 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 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 de entrada, 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 é equivalente a "Ei, continue usando o que você já tem!". Há poucos dados para transferir ao enviar esse tipo de resposta. Por isso, é mais rápido do que enviar uma cópia do recurso real 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 apenas o arquivo completo se o ETag do arquivo no servidor não corresponder ao valor If-None-Match do navegador. Nesse caso, os dois valores corresponderam, então o servidor retorna uma resposta 304 Not Modified com instruções sobre por quanto tempo o arquivo precisa ficar armazenado em cache (Cache-Control: max-age=120).

Resumo

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

As configurações de Cache-Control abaixo 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 podem ser armazenados em cache.
  • Cache-Control: max-age=31536000 para recursos com controle de versão.

Além disso, o cabeçalho ETag ou Last-Modified pode ajudar a revalidar recursos de cache expirados com mais eficiência.

Saiba mais

Se você quiser ir além dos conceitos básicos de uso do cabeçalho Cache-Control, confira o guia Práticas recomendadas de armazenamento em cache e golpes de idade máxima (em inglês) de Jake Archibald.

Consulte Ame seu cache para orientações sobre como otimizar o uso do cache para visitantes recorrentes.

Apêndice: mais dicas

Se você tiver mais tempo, aqui estão 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.
  • Minimize a desistência de usuários. Se parte de um recurso (como um arquivo CSS) for atualizada com frequência, enquanto o restante do arquivo não for atualizado (como o código da biblioteca), considere dividir o código com atualizações frequentes em um arquivo separado e usar uma estratégia de armazenamento em cache de curta duração para o código com atualização frequente e uma estratégia de armazenamento em cache longo para o código que não muda com frequência.
  • Confira a nova diretiva stale-while-revalidate se algum grau de inatividade for aceitável na sua política Cache-Control.

Apêndice: fluxograma de Cache-Control

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

Apêndice: exemplos de Cache-Control

Valor 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 1 ano.
no-store A resposta não pode ser armazenada em cache e deve ser buscada por completo em cada solicitação.