Os usuários pressionam o botão de voltar com mais frequência do que quase qualquer outro controle no navegador. Eles esperam que a tela anterior apareça imediatamente, exatamente onde a deixaram. Os navegadores modernos atendem a essa expectativa com o cache de voltar/avançar, ou bfcache. Em vez de destruir uma página quando você navega para longe, o navegador a congela na memória. Quando você retorna, ele restaura um snapshot. O navegador pula a análise do HTML, a reexecução do JavaScript e o recálculo do layout. O resultado parece instantâneo porque a página nunca morreu completamente.

O que o bfcache realmente faz

Um carregamento de página normal é caro. O navegador deve buscar recursos, tokenizar o HTML, construir o DOM, executar scripts, resolver estilos, realizar o layout, pintar pixels e compor camadas. O bfcache evita quase tudo isso mantendo a página viva em um estado congelado na RAM. Não é um cache de disco. A página renderizada, incluindo o heap do JavaScript, a posição de rolagem e o estado do formulário, permanece na memória enquanto o usuário lê a próxima página. Quando o usuário clica em voltar, o navegador "descongela" o snapshot e dispara um evento pageshow. A página retoma sem tocar na rede ou refazer o layout do zero. Para usuários em dispositivos lentos ou conexões instáveis, a diferença entre uma restauração do bfcache e um carregamento novo pode ser de centenas de milissegundos ou mais.

O que o quebra

Um desenvolvedor realizou recentemente um experimento limpo para descobrir exatamente o que bloqueia o bfcache. Eles criaram seis páginas simples, cada uma testando um possível bloqueador, navegaram para longe e pressionaram voltar. Os resultados foram claros.

Uma página de referência (baseline) sem cabeçalhos ou scripts incomuns foi restaurada com sucesso. Uma página com um listener de beforeunload também foi restaurada sem problemas. Surpreendentemente, uma página servida com Cache-Control: no-store também entrou no bfcache, contradizendo orientações antigas. Mesmo um artigo de blog ao vivo, que poderia parecer dinâmico demais para congelar, foi restaurado com sucesso.

Duas páginas falharam. Uma página com um listener de evento unload não pôde ser restaurada. Uma página com uma conexão WebSocket aberta também foi bloqueada. Essas duas falhas apontam para as armadilhas que pegam sites reais em produção todos os dias.

A armadilha do evento unload

O evento unload tem sido, há muito tempo, o sinal padrão para limpezas de última hora. Desenvolvedores o utilizam para enviar beacons de analytics, encerrar timers ou limpar estados temporários. O problema é que o bfcache é construído sobre a ideia de que a página pode voltar à vida. Se o navegador detectar um listener de unload, ele assume que a página espera uma destruição total e se recusa a congelá-la. Não importa se a função anexada está vazia. A mera presença do listener é suficiente para vetar o cache em todos os navegadores modernos.

A substituição é o pagehide. Este evento é disparado tanto quando a página está sendo congelada para o bfcache quanto quando está sendo realmente descartada. Se você precisar distinguir entre os dois, a propriedade event.persisted será true quando a página estiver indo para o bfcache. Para a maioria das tarefas de encerramento (teardown), no entanto, o pagehide cobre ambos os caminhos. Mova toda a lógica de limpeza de unload para pagehide. Em seguida, remova completamente todos os listeners de unload, incluindo aqueles escondidos em snippets de analytics de terceiros ou plugins legados.

Armadilhas de conexões ativas

Uma conexão de rede ou de armazenamento aberta sinaliza que sua página ainda está realizando trabalho real. O navegador faz um inventário dos recursos ativos no momento da navegação. Se encontrar um WebSocket aberto, uma conexão de peer WebRTC ativa ou uma conexão IndexedDB pendente, ele aborta o congelamento e encerra a página normalmente. O snapshot não pode ser confiável enquanto bytes ainda podem estar fluindo.

Você deve fechar esses recursos dentro de um listener de pagehide. Chame o método close do seu WebSocket. Encerre as conexões de peer WebRTC. Aborte ou confirme (commit) quaisquer transações IndexedDB pendentes. Se o seu aplicativo precisar desses canais quando o usuário retornar, reabra-os dentro do pageshow. Esse padrão "fechar no pagehide, restaurar no pageshow" mantém a página elegível para uma navegação de volta instantânea sem perder a funcionalidade.

A surpresa do no-store

Durante anos, o senso comum defendia que Cache-Control: no-store impedia o bfcache. O Chrome mudou esse comportamento em 2025. Uma página servida com no-store agora pode entrar no bfcache. O navegador só remove o snapshot congelado mais tarde se os estados de autenticação ou cookies mudarem de uma forma que invalide o estado salvo. Se você tem usado o no-store como