Como configurar o comportamento de armazenamento em cache HTTP

Neste codelab, mostramos como mudar os cabeçalhos de armazenamento em cache HTTP retornados por um servidor da Web baseado em Node.js, executando o framework de exibição Express. Ele também mostra como confirmar se o comportamento de armazenamento em cache esperado está sendo aplicado usando o painel Network no DevTools do Chrome.

Conhecer o projeto de exemplo

Estes são os principais arquivos com que você vai trabalhar no projeto de exemplo:

  • server.js contém o código Node.js que exibe o conteúdo do app da Web. Ele usa o Express para processar solicitações HTTP e respostas. Em particular, express.static() é usado para exibir todos os arquivos locais no diretório público, então a documentação do serve-static será útil.
  • public/index.html é o HTML do app da Web. Como a maioria dos arquivos HTML, ele não contém informações de controle de versões como parte do URL.
  • public/app.15261a07.js e public/style.391484cf.css são os recursos de JavaScript e CSS do app da Web. Cada um desses arquivos contém um hash no URL, correspondente ao conteúdo. O index.html é responsável por acompanhar qual URL com controle de versão específico deve ser carregado.

Configurar cabeçalhos de armazenamento em cache para nosso HTML

Ao responder a solicitações de URLs que não contêm informações de controle de versões, adicione Cache-Control: no-cache às mensagens de resposta. Além disso, é recomendável definir um dos dois cabeçalhos de resposta adicionais: Last-Modified ou ETag. O index.html se enquadra nessa categoria. É possível dividir isso em duas etapas.

Primeiro, os cabeçalhos Last-Modified e ETag são controlados pelas opções de configuração etag e lastModified. Ambas as opções usam true como padrão para todas as respostas HTTP. Portanto, nessa configuração atual, você não precisa ativar esse comportamento. Mas você pode ser explícito na configuração de qualquer maneira.

Em segundo lugar, você precisa adicionar o cabeçalho Cache-Control: no-cache, mas apenas para seus documentos HTML (index.html, neste caso). A maneira mais fácil de definir esse cabeçalho condicionalmente é escrever um setHeaders function personalizado e, nele, verificar se a solicitação recebida é para um documento HTML.

A configuração de veiculação estática em server.js começa assim:

app.use(express.static('public'));
  • Faça as mudanças descritas acima para que o resultado seja parecido com este:
app.use(express.static('public', {
  etag: true, // Just being explicit about the default.
  lastModified: true,  // Just being explicit about the default.
  setHeaders: (res, path) => {
    if (path.endsWith('.html')) {
      // All of the project's HTML files end in .html
      res.setHeader('Cache-Control', 'no-cache');
    }
  },
}));

Configurar cabeçalhos de armazenamento em cache para os URLs com controle de versão

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. app.15261a07.js e style.391484cf.css se enquadram nessa categoria.

Com base no setHeaders function usado na última etapa, é possível adicionar mais lógica para verificar se uma determinada solicitação é para um URL com controle de versão e, em caso afirmativo, adicionar o cabeçalho Cache-Control: max-age=31536000.

A maneira mais robusta de fazer isso é usar uma expressão regular para conferir se o recurso solicitado corresponde a um padrão específico que você sabe que os hashes seguem. No caso deste projeto de exemplo, são sempre oito caracteres do conjunto de dígitos de 0 a 9 e as letras minúsculas de a a f (ou seja, caracteres hexadecimais). O hash é sempre separado por um caractere . em ambos os lados.

Uma expressão regular que corresponde a essas regras gerais pode ser expressa como new RegExp('\\.[0-9a-f]{8}\\.').

  • Modifique a função setHeaders para que ela fique assim:
app.use(express.static('public', {
  etag: true, // Just being explicit about the default.
  lastModified: true,  // Just being explicit about the default.
  setHeaders: (res, path) => {
    const hashRegExp = new RegExp('\\.[0-9a-f]{8}\\.');

    if (path.endsWith('.html')) {
      // All of the project's HTML files end in .html
      res.setHeader('Cache-Control', 'no-cache');
    } else if (hashRegExp.test(path)) {
      // If the RegExp matched, then we have a versioned URL.
      res.setHeader('Cache-Control', 'max-age=31536000');
    }
  },
}));

Confirmar o novo comportamento usando o DevTools

Com as modificações no servidor de arquivos estáticos, você pode verificar se os cabeçalhos certos estão sendo definidos. Para isso, visualize o app em tempo real com o painel Network do DevTools aberto.

  • Personalize as colunas exibidas no painel Network para incluir as informações mais relevantes. Para isso, clique com o botão direito do mouse no cabeçalho da coluna:

Configuração do painel Network do DevTools.

Aqui, as colunas que merecem atenção são Name, Status, Cache-Control, ETag e Last-Modified.

  • Com o DevTools aberto no painel Network, atualize a página.

Depois que a página for carregada, você verá entradas no painel Network semelhantes a estas:

Colunas do painel Network.

A primeira linha é para o documento HTML que você acessou. Isso é veiculado corretamente com Cache-Control: no-cache. O status de resposta HTTP para essa solicitação é um 304. Isso significa que o navegador sabia que não deveria usar o HTML armazenado em cache imediatamente, mas fez uma solicitação HTTP ao servidor da Web, usando as informações Last-Modified e ETag para conferir se havia alguma atualização no HTML que já estava no cache. A resposta HTTP 304 indica que não há HTML atualizado.

As duas linhas seguintes são para os recursos de JavaScript e CSS com controle de versão. Você vai ver que eles são exibidos com Cache-Control: max-age=31536000, e o status HTTP de cada um é 200. Devido à configuração usada, nenhuma solicitação real está sendo feita ao servidor Node.js. Ao clicar na entrada, você verá mais detalhes, incluindo que a resposta veio "(do cache em disco)".

Um status de resposta de rede de 200.

Os valores reais das colunas ETag e Last-Modified não importam muito. O importante é confirmar se eles estão sendo definidos.

Resumindo

Depois de seguir as etapas deste codelab, você já sabe como configurar os cabeçalhos de resposta HTTP em um servidor da Web baseado em Node.js usando o Express para otimizar o uso do cache HTTP. Você também tem as etapas necessárias para confirmar que o comportamento de armazenamento em cache esperado está sendo usado, por meio do painel Network no DevTools do Chrome.