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

Buscar recursos pela rede é lento e caro:

  • Respostas grandes exigem muitas ida e volta entre o navegador e o servidor.
  • A página não será carregada até que todos os recursos essenciais sejam transferidos por download.
  • 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 é a primeira linha de defesa. Ela não é necessariamente a abordagem mais poderosa ou flexível, e você tem controle limitado sobre a vida útil das respostas armazenadas em cache, mas ela é 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

Na verdade, não há uma única API chamada de cache HTTP. É o nome geral de uma coleção de APIs da plataforma da Web. Essas APIs têm suporte em 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 o cache HTTP funciona

Todas as solicitações HTTP feitas pelo navegador são primeiro roteadas 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 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. No cenário ideal, você terá controle sobre o código do seu aplicativo da Web (que vai determinar os cabeçalhos de solicitação) e a configuração do servidor da Web (que vai determinar os cabeçalhos de resposta).

Consulte o artigo Armazenamento em cache HTTP do 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 de configurá-los em seu nome quando faz 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 sobre os valores atuais no cache HTTP.

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

Cabeçalhos de resposta: configure seu servidor da Web

A parte mais importante da configuração do cache HTTP são os cabeçalhos que o servidor da Web adiciona a cada resposta de saída. Os cabeçalhos a seguir influenciam 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 armazenada 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 será o mesmo e não será 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, em vez 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 não os incluem, a menos que você os configure explicitamente. Os detalhes específicos de como configurar cabeçalhos variam muito dependendo do servidor da Web que você usa. Consulte a documentação do servidor para obter os detalhes mais precisos.

Para economizar tempo, confira as instruções para configurar alguns servidores da Web conhecidos:

Omitir 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. Provavelmente você quer mais controle do que isso, então tire 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 os 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 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 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 esteja mais atualizada, conforme determinado por max-age ou expires, ou até que seja excluída do cache por algum outro motivo, como a limpeza do cache do navegador pelo usuário. Como resultado, diferentes usuários 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 os usuários que armazenaram em cache uma cópia anterior (mas ainda válida) usam uma versão mais antiga da resposta.

Como você consegue 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 fazer o download da nova resposta sempre que o conteúdo mudar. 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 versionamento e cujo conteúdo nunca muda, adicione Cache-Control: max-age=31536000 às suas respostas.

A configuraçã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 com suporte), ele poderá usar imediatamente o valor no cache HTTP, sem precisar fazer uma solicitação de rede para o servidor da Web. Ó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 de recursos.

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

Infelizmente, nem todos os URLs carregados têm versões. Talvez você não consiga incluir uma etapa de build antes de implantar o app da Web, então não é possível adicionar hashes aos URLs de recursos. Além disso, todo aplicativo da Web precisa de arquivos HTML, que quase nunca incluem informações de controle de versão, já que ninguém vai se incomodar em usar seu app da Web se precisar lembrar que o URL a ser visitado é 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 cache HTTP sozinho não é poderoso o suficiente para evitar a rede completamente. Não se preocupe, você vai aprender em breve sobre os service workers, que vão oferecer o suporte necessário para você vencer a batalha. No entanto, há algumas etapas que você pode seguir para garantir que as solicitações de rede sejam o 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. Isso instrui o navegador a ser revalidado 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 podem.
  • 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. O Cache-Control também pode aceitar uma lista de diretivas separada por vírgulas. Consulte o Apêndice: exemplos de Cache-Control.

Configurar 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.

Suponha que 120 segundos se passaram 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. Infelizmente, 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 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 criados para resolver. O servidor gera e retorna um token arbitrário, que normalmente é um hash ou alguma 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á-la ao servidor na próxima solicitação. Se a impressão digital ainda for a mesma, o recurso não terá mudado e o navegador poderá pular o download.

A configuração de ETag ou Last-Modified torna a solicitação de nova validaçã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 detecta esses cabeçalhos de solicitação, 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 a serem transferidos ao enviar esse tipo de resposta, então ela geralmente é muito mais rápida do que enviar uma cópia do recurso solicitado.

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 correspondem, então o servidor retorna uma resposta 304 Not Modified com instruções sobre quanto tempo o arquivo deve permanecer 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 exige muito trabalho para ser configurado.

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 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 armadilhas de idade máxima de Jake Archibald.

Consulte Aproveite o 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, esse conteúdo será buscado e armazenado várias vezes.
  • Minimizar a desistência. Se parte de um recurso (como um arquivo CSS) for atualizado com frequência, enquanto o restante do arquivo não for (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 duração longa para o código que não muda com frequência.
  • Confira a nova diretiva stale-while-revalidate se algum grau de desatuação for aceitável na sua política de Cache-Control.

Apêndice: fluxograma 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 pelos 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é 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 por completo em cada solicitação.