Amo seu cache ❤️

Os usuários que carregarem seu site pela segunda vez vão usar o cache HTTP. Portanto, verifique se ele está funcionando bem.

Esta postagem é complementar ao vídeo Ame seu cache, parte do conteúdo estendido do Chrome Dev Summit 2020. Não deixe de conferir o vídeo:

Quando os usuários carregam seu site pela segunda vez, o navegador deles usa recursos no cache HTTP para ajudar a acelerar o carregamento. No entanto, os padrões de armazenamento em cache na Web datam de 1999 e são definidos de forma bastante ampla. Determinar se um arquivo, como CSS ou uma imagem, pode ser buscado novamente da rede em vez de ser carregado do cache é uma ciência um pouco imprecisa.

Nesta postagem, vou falar sobre um padrão moderno e sensato para armazenamento em cache, que na verdade não faz armazenamento em cache. Mas esse é apenas o padrão, e é claro que ele tem mais nuances do que apenas "desativar". Siga em frente com a leitura.

Metas

Quando um site é carregado pela segunda vez, você tem dois objetivos:

  1. Garantir que os usuários tenham a versão mais atualizada disponível. Se você fez alguma alteração, isso deve ser refletido rapidamente.
  2. Faça a #1 enquanto extrai o mínimo possível da rede

No sentido mais amplo, você só quer enviar a menor mudança para seus clientes quando eles carregarem o site novamente. Além disso, estruturar o site para garantir a distribuição mais eficiente de qualquer mudança é um desafio. Saiba mais sobre isso abaixo e no vídeo.

Dito isso, você também tem outras opções ao considerar o armazenamento em cache. Talvez você tenha decidido deixar o cache HTTP do navegador de um usuário reter seu site por um longo tempo para que nenhuma solicitação de rede seja necessária para exibi-lo. Ou você criou um service worker que vai veicular um site totalmente off-line antes de verificar se ele está atualizado. Essa é uma opção extrema, válida e usada em muitas experiências da Web com foco em apps off-line, mas a Web não precisa estar em um extremo de cache ou até mesmo de rede.

Contexto

Como desenvolvedores da Web, estamos acostumados com a ideia de ter um "cache desatualizado". Mas sabemos, quase por instinto, quais são as ferramentas disponíveis para resolver isso: faça uma "atualização forçada", abra uma janela anônima ou use uma combinação das ferramentas para desenvolvedores do seu navegador para limpar os dados de um site.

Os usuários comuns da Internet não têm esse luxo. Embora tenhamos algumas metas principais de garantir que nossos usuários se divirtam com o segundo carregamento, também é muito importante garantir que eles não tenham uma experiência ruim ou fiquem presos. Confira o vídeo para saber como quase bloqueamos o site web.dev/live.

Para entender melhor, um motivo muito comum para "cache desatualizado" é o padrão da era de 1999 para armazenamento em cache. Ele depende do cabeçalho Last-Modified:

Diagrama mostrando por quanto tempo os diferentes recursos são armazenados em cache pelo navegador de um usuário
Os recursos gerados em momentos diferentes (em cinza) serão armazenados em cache em momentos diferentes. Assim, uma segunda carga pode receber uma combinação de recursos novos e em cache

Cada arquivo carregado é mantido por mais 10% da vida útil atual, conforme o navegador o percebe. Por exemplo, se index.html foi criado há um mês, ele será armazenado em cache pelo navegador por mais três dias.

Essa ideia era bem intencional no passado, mas devido à natureza fortemente integrada dos sites atuais, esse comportamento padrão significa que é possível entrar em um estado em que um usuário tem arquivos projetados para versões diferentes do site (por exemplo, o JS da versão de terça-feira e o CSS do lançamento de sexta-feira), tudo porque esses arquivos não foram atualizados exatamente ao mesmo tempo.

O caminho bem iluminado

Um padrão moderno para o armazenamento em cache é não fazer nenhum armazenamento em cache e usar CDNs para aproximar o conteúdo dos usuários. Sempre que um usuário carrega seu site, ele acessa a rede para ver se ele está atualizado. Essa solicitação terá baixa latência, porque será fornecida por um CDN geograficamente próximo a cada usuário final.

É possível configurar seu host da Web para responder a solicitações da Web com este cabeçalho:

Cache-Control: max-age=0,must-revalidate,public

Isso basicamente significa que o arquivo não é válido por nenhum período e precisa ser validado pela rede antes de ser usado novamente. Caso contrário, ele será apenas "sugerido".

Esse processo de validação é relativamente barato em termos de bytes transferidos. Se um arquivo de imagem grande não tiver mudado, seu navegador vai receber uma pequena resposta 304, mas isso custa latência, já que o usuário ainda precisa acessar a rede para descobrir isso. Essa é a principal desvantagem dessa abordagem. Ele pode funcionar muito bem para pessoas com conexões rápidas no primeiro mundo e onde sua CDN preferida tem uma ótima cobertura, mas não para pessoas que usam conexões móveis mais lentas ou uma infraestrutura de baixa qualidade.

De qualquer forma, essa é uma abordagem moderna que é o padrão em um CDN conhecido, o Netlify, mas pode ser configurada em quase qualquer CDN. No Firebase Hosting, é possível incluir este cabeçalho na seção de hospedagem do arquivo firebase.json:

"headers": [
  // Be sure to put this last, to not override other headers
  {
    "source": "**",
    "headers": [ {
      "key": "Cache-Control",
      "value": "max-age=0,must-revalidate,public"
    }
  }
]

Embora eu ainda sugira isso como um padrão sensato, é apenas isso: o padrão. Continue lendo para saber como fazer upgrade dos padrões.

URLs com impressão digital

Ao incluir um hash do conteúdo do arquivo no nome de recursos, imagens e assim por diante no site, você garante que esses arquivos sempre terão conteúdo exclusivo. Isso resultará em arquivos com o nome sitecode.af12de.js, por exemplo. Quando o servidor responder às solicitações desses arquivos, você poderá instruir com segurança os navegadores do usuário final para armazenar em cache por um longo período, configurando-os com este cabeçalho:

Cache-Control: max-age=31536000,immutable

Esse valor é um ano, em segundos. E, de acordo com a especificação, isso é igual a "sempre".

É importante ressaltar que não gere esses hashes manualmente. É muito trabalho manual. Você pode usar ferramentas como Webpack, Rollup e assim por diante para ajudar com isso. Leia mais sobre eles no Relatório de ferramentas.

Não é apenas o JavaScript que pode se beneficiar de URLs com impressão digital. Recursos como ícones, CSS e outros arquivos de dados imutáveis também podem ser nomeados dessa forma. Assista ao vídeo acima para saber mais sobre a divisão de código, que permite enviar menos código sempre que o site muda.

Independentemente de como seu site aborda o armazenamento em cache, esses tipos de arquivos com impressão digital são incrivelmente valiosos para qualquer site que você possa criar. A maioria dos sites não muda em todas as versões.

É claro que não podemos renomear nossas páginas "amigáveis" voltadas ao usuário dessa maneira: renomeie o arquivo index.html para index.abcd12.html. Isso é inviável, não é possível pedir aos usuários para acessar um novo URL sempre que eles carregam seu site. Esses URLs "amigáveis" não podem ser renomeados e armazenados em cache dessa maneira, o que me leva a um possível meio-termo.

O meio-termo

Obviamente, há um meio termo quando se trata de armazenamento em cache. Apresentei duas opções extremas: cache nunca ou cache para sempre. Além disso, haverá vários arquivos que podem ser armazenados em cache por um tempo, como os URLs "amigáveis" mencionados acima.

Se você quiser armazenar em cache esses URLs "amigáveis" e o HTML deles, vale a pena considerar quais dependências eles incluem, como eles podem ser armazenados em cache e como o armazenamento em cache dos URLs por um tempo pode afetar você. Vamos analisar uma página HTML que inclui uma imagem como esta:

<img src="/images/foo.jpeg" loading="lazy" />

Se você atualizar ou alterar seu site excluindo ou alterando essa imagem com carregamento lento, os usuários que visualizarem uma versão em cache do HTML poderão receber uma imagem incorreta ou ausente porque ainda armazenaram em cache o /images/foo.jpeg original quando voltaram ao site.

Se você tomar cuidado, isso não vai afetar você. Mas, de modo geral, é importante lembrar que o site, quando armazenado em cache pelos usuários finais, não existe mais apenas nos seus servidores. Em vez disso, ele pode existir em partes nos caches dos navegadores do usuário final.

Em geral, a maioria dos guias sobre armazenamento em cache fala sobre esse tipo de configuração. Você quer armazenar em cache por uma hora, várias horas e assim por diante? Para definir esse tipo de cache, use um cabeçalho como este (que armazena em cache por 3600 segundos ou uma hora):

Cache-Control: max-age=3600,immutable,public

Um último ponto. Se você estiver criando conteúdo oportuno que normalmente só pode ser acessado pelos usuários uma vez, como artigos de notícias, minha opinião é que eles nunca devem ser armazenados em cache, e você deve usar o padrão sensato acima. Muitas vezes, superestimamos o valor do armazenamento em cache em relação ao desejo do usuário de sempre ver o conteúdo mais recente e melhor, como uma atualização importante de uma notícia ou um evento atual.

Opções que não são HTML

Além do HTML, algumas outras opções para arquivos que ficam no meio-termo incluem:

  • Em geral, procure recursos que não afetem outros

    • Por exemplo: evite o CSS, porque ele causa mudanças na forma como o HTML é renderizado.
  • Imagens grandes usadas em artigos oportunos

    • Os usuários provavelmente não vão visitar um único artigo mais do que algumas vezes. Portanto, não armazene fotos ou imagens principais em cache para sempre e gaste armazenamento.
  • Um recurso que representa algo que tem vida útil

    • Os dados JSON sobre o clima podem ser publicados apenas a cada hora. Portanto, armazene em cache o resultado anterior por uma hora. Ele não vai mudar na janela
    • Os builds de um projeto de código aberto podem ter uma taxa limitada. Portanto, armazene em cache uma imagem de status de build até que o status possa mudar

Resumo

Quando os usuários carregam seu site pela segunda vez, você já tem um voto de confiança: eles querem voltar e receber mais do que você oferece. Nesse momento, não se trata apenas de reduzir o tempo de carregamento. Você tem várias opções disponíveis para garantir que o navegador faça apenas o trabalho necessário para oferecer uma experiência rápida e atualizada.

O armazenamento em cache não é um conceito novo na Web, mas talvez precise de um padrão padrão. Considere usar um e ative as melhores estratégias de armazenamento em cache quando precisar. Agradecemos por ler.

Consulte também

Para um guia geral sobre o cache HTTP, consulte Impedir solicitações de rede desnecessárias com o cache HTTP.