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 carregará até que o download de todos os recursos essenciais seja concluído.
  • Se um usuário no seu site tiver um plano de dados móveis limitado, todas as solicitações de rede desnecessárias serão um desperdício de dinheiro.

Como evitar solicitações de rede desnecessárias? O cache HTTP do navegador é a 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. No entanto, ela é eficaz, compatível com todos os navegadores e não exige muito trabalho.

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

Compatibilidade com navegadores

O cache de HTTP é o nome geral de um conjunto de APIs de plataforma da Web compatíveis com todos os navegadores:

Cache-Control

Compatibilidade com navegadores

  • Verdadeiro
  • 12
  • Verdadeiro
  • Verdadeiro

Origem

ETag

Compatibilidade com navegadores

  • Verdadeiro
  • 12
  • Verdadeiro
  • Verdadeiro

Origem

Last-Modified

Compatibilidade com navegadores

  • Verdadeiro
  • 12
  • Verdadeiro
  • Verdadeiro

Origem

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 armazenada em 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 da 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ê tem controle sobre o código do app da Web, que determina os cabeçalhos da solicitação, e sobre a configuração do servidor da Web, que determina os cabeçalhos de resposta.

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

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

Há vários cabeçalhos importantes que precisam ser incluídos nas solicitações de saída do app da Web, mas o navegador quase sempre se encarrega de configurá-los para você ao fazer solicitações. Os cabeçalhos de solicitação que afetam a verificação da atualização, como If-None-Match e If-Modified-Since, aparecem com base na compreensão do navegador sobre os valores atuais no cache HTTP.

Boa notícia é que você pode continuar incluindo tags como <img src="my-image.png"> no HTML, e o navegador cuida automaticamente do armazenamento em cache HTTP sem esforço extra.

Cabeçalhos de resposta: configurar seu servidor da Web

A parte da configuração de 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 determinam o comportamento efetivo 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 armazenarão a resposta individual em cache.
ETag.
Quando o navegador encontra uma resposta expirada em cache, 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 será o mesmo e não será necessário fazer o download dele 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 mudou, em vez da estratégia baseada em conteúdo de ETag.

Alguns servidores da Web têm suporte integrado para a definição desses cabeçalhos por padrão. Outros não incluem os cabeçalhos, 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 que você usa. Consulte a documentação do seu servidor para ver os detalhes mais precisos.

Para que você não precise pesquisar nada, aqui estão instruções para configurar alguns servidores da Web conhecidos:

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 determinado tipo de conteúdo. É provável que você queira mais controle do que isso oferece, então precisará de 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 abordar ao configurar os cabeçalhos de resposta do servidor da Web.

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

Como os URLs com controle de versões 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 invalidez das respostas armazenadas em cache.

Suponha que o servidor instrua os navegadores a armazenar um arquivo CSS em cache por um ano (Cache-Control: max-age=31536000), mas o designer acabou de fazer uma atualização de emergência que precisa ser implementada imediatamente. Como você notifica os navegadores para que atualizem a cópia "desatualizada" em cache do arquivo? 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 ela seja removida do cache por algum outro motivo, como a limpeza do cache do navegador. Como resultado, usuários diferentes podem acabar carregando versões diferentes do arquivo quando a página é criada: os que acabaram de buscar o recurso usam a nova versão, mas os que armazenaram uma cópia anterior (mas ainda válida) usam uma versão mais antiga.

Para receber o armazenamento em cache do lado do cliente e as atualizações rápidas, altere o URL do recurso e force o usuário a fazer o download da nova resposta sempre que o conteúdo dela for 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 informações de "impressão digital" ou controle de versões e cujo conteúdo nunca tem a finalidade de mudar, adicione Cache-Control: max-age=31536000 às respostas.

A definição desse valor informa ao navegador que, quando precisar carregar o mesmo URL a qualquer momento durante o 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 ao seu servidor da Web. Isso é ótimo, você ganhou imediatamente a confiabilidade e a velocidade de evitar a rede.

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

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

Infelizmente, nem todos os URLs que você carrega têm controle de versão. Talvez não seja possível incluir uma etapa de build antes de implantar o app da Web, ou seja, não será possível adicionar hashes aos URLs dos recursos. E todo aplicativo da Web precisa de arquivos HTML, que quase nunca incluem informações de controle de versão, porque ninguém vai 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?

O armazenamento em cache HTTP por si só não é suficiente para evitar completamente a rede. Não se preocupe, você aprenderá em breve sobre os service workers, que oferecem mais suporte. No entanto, você pode seguir algumas etapas para garantir que as solicitações de rede sejam as mais rápidas e eficientes possível.

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

  • no-cache informa ao navegador que é necessário revalidar com o servidor todas as vezes antes de usar uma versão em cache do URL.
  • no-store instrui o navegador e outros caches intermediários (como CDNs) a nunca armazenarem nenhuma versão do arquivo.
  • private: os navegadores podem armazenar o arquivo em cache, mas os caches intermediários não.
  • public: qualquer cache pode armazenar a resposta.

Consulte o Apêndice: fluxograma Cache-Control para ver o processo de decisão de 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 de Cache-Control.

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

Exemplo de ETag

Suponha que se passaram 120 segundos 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. O navegador não pode usar a resposta anterior porque ela expirou. Nesse ponto, o navegador pode enviar uma nova solicitação e buscar a nova resposta completa. No entanto, isso é ineficiente porque, se o recurso não tiver sido alterado, não há motivo para fazer o download novamente das informações que já estão no cache.
Esse é o problema que os tokens de validação do 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 de 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 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á enviar uma resposta HTTP 304 Not Modified, que é equivalente a "Ei, continue usando o que já tem!" Há pouquíssimos dados para transferir ao enviar esse tipo de resposta. Portanto, isso geralmente é muito mais rápido do que ter que enviar uma cópia do recurso real que está sendo solicitado.

Um diagrama 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 valores são correspondentes, então o servidor retorna uma resposta 304 Not Modified com instruções sobre por quanto tempo o arquivo precisa ser 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. Ele pode ser usado em todos os navegadores e não exige muito trabalho para ser configurado.

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.

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 do básico do uso do cabeçalho Cache-Control, consulte o guia Práticas recomendadas de armazenamento em cache e gotchas max-age (em inglês) de Jake Archibald.

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

Apêndice: mais dicas

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

  • Use URLs consistentes. Se o mesmo conteúdo é exibido em URLs diferentes, o navegador busca e armazena esse conteúdo várias vezes.
  • Minimize a desistência. Se parte de um recurso (como um arquivo CSS) for atualizada com frequência, enquanto o restante do arquivo não (como no código da biblioteca), divida o código de atualização frequente em um arquivo separado e use uma estratégia de armazenamento curto 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.
  • Se algum grau de desatualização for aceitável na política Cache-Control, considere a nova diretiva stale-while-revalidate .

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 Cache-Control Explicação
max-age=86400 A resposta pode ser armazenada em cache por navegadores e caches intermediários por até um 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é dez 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 totalmente em cada solicitação.