O cache de avanço e retorno (ou bfcache) é uma otimização de navegador que permite a navegação instantânea para frente e para trás. Isso melhora significativamente a experiência de navegação, especialmente para usuários com redes ou dispositivos mais lentos.
Esta página descreve como otimizar suas páginas para o bfcache em todos os navegadores.
Compatibilidade com navegadores
O bfcache é compatível com o Firefox e o Safari há muitos anos, em computadores e dispositivos móveis.
A partir da versão 86, o Chrome ativou o bfcache para navegações entre sites no Android para uma pequena porcentagem de usuários. Nas versões subsequentes, o suporte adicional será lançado lentamente. Desde a versão 96, o bfcache está ativado para todos os usuários do Chrome em computadores e dispositivos móveis.
Noções básicas do bfcache
O bfcache é um cache na memória que armazena um snapshot completo de uma página à medida que o usuário navega. Com a página inteira na memória, o navegador poderá restaurá-la rapidamente se o usuário decidir retornar, em vez de precisar repetir todas as solicitações de rede necessárias para carregar a página.
O vídeo a seguir mostra o quanto o bfcache pode acelerar a navegação:
Os dados de uso do Chrome mostram que 1 em 10 navegações no computador e 1 em cada 5 em dispositivos móveis são de avanço e retorno. Por isso, o bfcache tem o potencial de economizar muito tempo e uso de dados.
Como funciona o "cache"
O "cache" usado pelo bfcache é diferente do cache HTTP, que desempenha seu próprio papel para acelerar navegações repetidas. O bfcache é um snapshot de toda a página na memória, incluindo a heap JavaScript, enquanto o cache HTTP contém apenas as respostas das solicitações feitas anteriormente. Como é muito raro que todas as solicitações necessárias para carregar uma página sejam compatíveis com o cache HTTP, visitas repetidas usando restaurações bfcache são sempre mais rápidas do que até mesmo as navegações sem bfcache mais otimizadas.
No entanto, criar um snapshot de uma página na memória envolve certa complexidade em relação à melhor forma de preservar o código em andamento. Por exemplo, como você lida com chamadas setTimeout()
em que o tempo limite é atingido enquanto a página está no bfcache?
A resposta é que os navegadores pausam todos os timers pendentes ou promessas não resolvidas para páginas no bfcache, incluindo quase todas as tarefas pendentes nas filas de tarefas do JavaScript, e retomam as tarefas de processamento se a página for restaurada do bfcache.
Em alguns casos, como tempos limite e promessas, isso é um risco razoavelmente baixo, mas em outros pode levar a um comportamento confuso ou inesperado. Por exemplo, se o navegador pausar uma tarefa necessária para uma transação do IndexedDB, isso poderá afetar outras guias abertas na mesma origem, porque os mesmos bancos de dados do IndexedDB podem ser acessados por várias guias simultaneamente. Como resultado, os navegadores geralmente não tentam armazenar páginas em cache no meio de uma transação do IndexedDB ou ao usar APIs que possam afetar outras páginas.
Para ver mais detalhes sobre como o uso da API afeta a qualificação para o bfcache de uma página, consulte Otimizar suas páginas para o bfcache.
O bfcache e os apps de página única (SPA)
Como o bfcache funciona com navegações gerenciadas pelo navegador, ele não funciona para "navegações suaves" em um app de página única (SPA). No entanto, o bfcache ainda pode ajudar ao sair e retornar a um SPA.
APIs para observar o bfcache
Embora o bfcache seja uma otimização automática feita pelos navegadores, ainda é importante que os desenvolvedores saibam quando isso está acontecendo para que possam otimizar as páginas e ajustar qualquer métrica ou medição de desempenho adequadamente.
Os principais eventos usados para observar o bfcache são os eventos de transição de página pageshow
e pagehide
, que têm suporte da maioria dos navegadores.
Os eventos mais recentes do ciclo de vida da página, freeze
e resume
, também são enviados quando as páginas entram ou saem do bfcache e em algumas outras situações, por exemplo, quando uma guia em segundo plano
é congelada para minimizar o uso da CPU. Esses eventos só são compatíveis com navegadores baseados no Chromium.
Observar quando uma página é restaurada do bfcache
O evento pageshow
é disparado logo após o load
quando a página está sendo carregada pela primeira vez e sempre que ela é restaurada do bfcache. O evento pageshow
tem uma propriedade
persisted
, que será true
se a página tiver sido restaurada do bfcache e false
caso contrário. É possível usar a propriedade persisted
para distinguir os carregamentos de página regulares das restaurações do bfcache. Exemplo:
window.addEventListener('pageshow', (event) => {
if (event.persisted) {
console.log('This page was restored from the bfcache.');
} else {
console.log('This page was loaded normally.');
}
});
Em navegadores compatíveis com a API Page Lifecycle, o evento resume
é disparado quando as páginas são restauradas do bfcache (imediatamente antes do evento pageshow
) e quando um usuário acessa novamente uma guia em segundo plano congelada. Se você quiser atualizar o estado de uma página depois que ela for congelada (o que inclui páginas no bfcache), poderá usar o evento resume
. No entanto, se quiser medir a taxa de ocorrências do bfcache do site, será necessário usar o evento pageshow
. Em alguns casos, talvez seja necessário usar ambos.
Para detalhes sobre as práticas recomendadas de medição do bfcache, consulte Como o bfcache afeta a análise e a medição de desempenho.
Observar quando uma página está entrando no bfcache
O evento pagehide
é disparado quando uma página é descarregada ou quando o navegador tenta
colocá-la no bfcache.
O evento pagehide
também tem uma propriedade persisted
. Se for false
, pode ter certeza de que a página não vai entrar no bfcache. No entanto, o fato de persisted
ser true
não garante que uma página será armazenada em cache. Isso significa
que o navegador intends armazenar a página em cache, mas pode haver outros fatores
que impossibilitam o armazenamento em cache.
window.addEventListener('pagehide', (event) => {
if (event.persisted) {
console.log('This page *might* be entering the bfcache.');
} else {
console.log('This page will unload normally and be discarded.');
}
});
Da mesma forma, o evento freeze
é disparado imediatamente após o evento pagehide
se persisted
for true
, mas isso significa que o navegador intends armazenar a página em cache. Talvez seja necessário descartá-lo por vários motivos explicados posteriormente.
Otimizar suas páginas para o bfcache
Nem todas as páginas são armazenadas no bfcache e, mesmo quando uma página é armazenada lá, ela não fica lá indefinidamente. As páginas a seguir descrevem o que torna as páginas qualificadas para o uso do bfcache e recomendam práticas recomendadas para maximizar a capacidade do navegador de armazenar a página em cache e melhorar as taxas de ocorrência em cache.
Usar pagehide
em vez de unload
A maneira mais importante de otimizar para o bfcache em todos os navegadores é nunca usar
listeners de eventos unload
. Detecte pagehide
, porque ele é acionado
quando uma página entra no bfcache e sempre que unload
é acionado.
unload
é um recurso mais antigo, criado para ser acionado sempre que um usuário
sair de uma página. Esse
não é mais o caso,
mas muitas páginas da Web ainda operam com o pressuposto de que os navegadores usam unload
dessa maneira e que, após o acionamento de unload
, a página descarregada deixa de existir.
Isso poderá corromper o bfcache se o navegador tentar armazenar uma página descarregada em cache.
No computador, o Chrome e o Firefox tornam as páginas com listeners unload
não qualificadas para
o bfcache, o que reduz os riscos, mas também faz com que muitas páginas não sejam armazenadas em cache e,
por isso, a atualização fique muito mais lenta. O Safari tenta armazenar em cache algumas páginas com listeners de eventos unload
.
No entanto, para reduzir possíveis falhas, ele não executa o evento unload
quando um usuário sai da página, o que torna os listeners de unload
não confiáveis.
Em dispositivos móveis, o Chrome e o Safari tentam armazenar páginas em cache com listeners de eventos unload
,
porque a falta de confiabilidade do unload
nesses dispositivos diminui o risco de falha.
O Firefox para dispositivos móveis trata as páginas que usam unload
como não qualificadas para o bfcache,
exceto no iOS, que exige que todos os navegadores usem o mecanismo de renderização WebKit.
Por isso, ele se comporta como o Safari.
Para determinar se qualquer JavaScript nas suas páginas usa unload
, recomendamos
usar a auditoria no-unload-listeners
no Lighthouse.
Para informações sobre o plano do Chrome de descontinuar unload
, consulte
Suspensão do uso do evento de descarregamento.
Usar a política de permissão para evitar que gerenciadores de descarregamento sejam usados em uma página
Alguns scripts e extensões de terceiros podem adicionar gerenciadores de descarregamento a uma página, tornando o site desagradável, tornando-o desqualificado para o bfcache. Para evitar isso no Chrome 115 e em versões mais recentes, use uma política de permissões.
Permission-Policy: unload()
Adicione apenas listeners beforeunload
condicionalmente
O evento beforeunload
não impede a qualificação das páginas para o cache de avanço e retorno.
No entanto, ela não é confiável. Por isso, recomendamos o uso apenas quando for absolutamente necessário.
Um exemplo de caso de uso do beforeunload
é avisar ao usuário que ele tem mudanças
não salvas que serão perdidas se sair da página. Nesse caso, recomendamos adicionar
listeners beforeunload
somente quando um usuário tiver mudanças não salvas
e removê-los imediatamente após as alterações não salvas serem salvas, como no
código a seguir:
function beforeUnloadListener(event) {
event.preventDefault();
return event.returnValue = 'Are you sure you want to exit?';
};
// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
window.addEventListener('beforeunload', beforeUnloadListener);
});
// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
window.removeEventListener('beforeunload', beforeUnloadListener);
});
Minimizar o uso de Cache-Control: no-store
Cache-Control: no-store
é um servidor da Web de cabeçalho HTTP que pode ser definido em respostas
que instrui o navegador a não armazenar a resposta em nenhum cache HTTP. É
usado em recursos que contêm informações sensíveis do usuário, como páginas por trás de um
login.
Embora o bfcache não seja um cache HTTP, os navegadores costumam excluir páginas do bfcache quando Cache-Control: no-store
está definido no recurso da página, mas não em qualquer sub-recurso. O Chrome está trabalhando para
mudar esse comportamento
e manter a privacidade do usuário, mas, por padrão, as páginas que usam
Cache-Control: no-store
não estão qualificadas para o bfcache.
Para otimizar o bfcache, use Cache-Control: no-store
somente em páginas que contenham
informações confidenciais que não podem ser armazenadas em cache.
Para páginas que querem sempre veicular conteúdo atualizado, mas não incluem
informações confidenciais, use Cache-Control: no-cache
ou
Cache-Control: max-age=0
. Elas instruem o navegador a revalidar o conteúdo antes de exibi-lo e não afetam a qualificação para o bfcache de uma página, já que a restauração de uma página do bfcache não envolve o cache HTTP.
Se o conteúdo mudar a cada minuto, busque atualizações usando o evento pageshow
para manter a página atualizada, conforme descrito na próxima seção.
Atualizar dados desatualizados ou confidenciais após a restauração do bfcache
Se o site mantiver dados de estado do usuário, e especialmente se esses dados incluírem informações confidenciais dele, eles precisarão ser atualizados ou apagados depois que uma página for restaurada do bfcache.
Por exemplo, se um usuário sai de um site em um computador público e o próximo usuário clica no botão "Voltar", os dados desatualizados do bfcache podem incluir dados particulares que o primeiro usuário esperava que fossem apagados ao sair.
Para evitar situações como essa, sempre atualize a página após um evento pageshow
se event.persisted
for true
:
window.addEventListener('pageshow', (event) => {
if (event.persisted) {
// Do any checks and updates to the page
}
});
Para algumas mudanças, convém forçar uma atualização completa, preservando
o histórico de navegação para frente. O código a seguir verifica a presença de um cookie específico do site no evento pageshow
e recarrega se o cookie não for encontrado:
window.addEventListener('pageshow', (event) => {
if (event.persisted && !document.cookie.match(/my-cookie)) {
// Force a reload if the user has logged out.
location.reload();
}
});
Restauração de anúncios e bfcache
Pode ser tentador tentar evitar o uso do bfcache para que sua página possa veicular um novo conjunto de anúncios em cada navegação de retorno ou avanço. No entanto, isso é ruim para o desempenho do site e não aumenta o engajamento com o anúncio de maneira consistente. Por exemplo, um usuário pode ter a intenção de retornar a uma página para clicar em um anúncio, mas, se a página for recarregada em vez de restaurada do bfcache, um anúncio diferente poderá ser mostrado. Recomendamos usar o teste A/B para determinar a melhor estratégia para sua página.
Para sites que querem atualizar os anúncios na restauração do bfcache, é possível atualizar apenas os anúncios no evento pageshow
quando event.persisted
for true
, sem afetar o desempenho da página, como neste exemplo da tag do Google Publishing.
Para mais informações sobre as práticas recomendadas para seu site, consulte seu provedor
de anúncios.
Evitar referências window.opener
Em navegadores mais antigos, se uma página fosse aberta usando
window.open()
em um link com
target=_blank
,
sem especificar
rel="noopener"
,
a página de abertura teria uma referência ao objeto da janela da página aberta.
Além de ser um risco de segurança,
uma página com uma referência não nula window.opener
não pode ser colocada com segurança em bfcache, porque isso pode corromper quaisquer páginas
que tentarem acessá-la.
Para evitar esses riscos, use rel="noopener"
para impedir a criação de
referências de window.opener
. Esse é o comportamento padrão em todos os navegadores mais recentes.
Caso seu site precise abrir uma janela e controlá-la usando
window.postMessage()
ou referenciando diretamente o objeto "window", nem a janela aberta nem o
abridor estão qualificados para o bfcache.
Feche as conexões abertas antes que o usuário saia da página
Como mencionado anteriormente, quando uma página é colocada no bfcache, ela pausa todas as tarefas JavaScript programadas e as retoma quando a página é retirada do cache.
Se essas tarefas programadas do JavaScript acessarem apenas APIs do DOM ou outras APIs isoladas à página atual, não vai causar problemas pausar essas tarefas enquanto a página não estiver visível para o usuário.
No entanto, se essas tarefas estiverem conectadas a APIs que também podem ser acessadas por outras páginas na mesma origem (por exemplo: IndexedDB, Web Locks e WebSockets), pausá-las poderá interromper a execução dessas páginas, impedindo que o código delas seja executado.
Como resultado, alguns navegadores não tentarão colocar uma página em bfcache se ela tiver uma das seguintes opções:
- Uma conexão IndexedDB aberta
- fetch() ou XMLHttpRequest em andamento
- Uma conexão aberta do WebSocket ou WebRTC
Caso sua página esteja usando uma dessas APIs, é recomendável fechar
as conexões e remover ou desconectar os observadores durante o evento pagehide
ou
freeze
. Isso permite que o navegador armazene a página em cache com segurança sem o risco de afetar outras guias abertas. Em seguida, se a página for restaurada do
bfcache, será possível reabrir ou reconectar essas APIs durante o evento
pageshow
ou resume
.
O exemplo a seguir mostra como garantir que as páginas que usam o IndexedDB estejam
qualificadas para o bfcache fechando uma conexão aberta no listener de eventos
pagehide
:
let dbPromise;
function openDB() {
if (!dbPromise) {
dbPromise = new Promise((resolve, reject) => {
const req = indexedDB.open('my-db', 1);
req.onupgradeneeded = () => req.result.createObjectStore('keyval');
req.onerror = () => reject(req.error);
req.onsuccess = () => resolve(req.result);
});
}
return dbPromise;
}
// Close the connection to the database when the user leaves.
window.addEventListener('pagehide', () => {
if (dbPromise) {
dbPromise.then(db => db.close());
dbPromise = null;
}
});
// Open the connection when the page is loaded or restored from bfcache.
window.addEventListener('pageshow', () => openDB());
Testar para garantir que suas páginas podem ser armazenadas em cache
O Chrome DevTools pode ajudar você a testar suas páginas para garantir que elas estejam otimizadas para o bfcache e identificar problemas que possam impedi-las de serem qualificadas.
Para testar uma página:
- Acesse a página no Chrome.
- No DevTools, acesse Aplicativo > Cache de avanço e retorno.
- Clique no botão Run Test. Em seguida, o DevTools tenta sair e voltar para determinar se a página pode ser restaurada do bfcache.
Se o teste for bem-sucedido, o painel vai exibir a mensagem "Restaurado do cache de avanço e retorno". Se ela não for bem-sucedida, o painel vai indicar o motivo. Para ver uma lista completa de motivos, consulte Lista de motivos não restaurados do Chromium.
Se o motivo for algo que você possa abordar como desenvolvedor, o painel o marcará como Acionável.
Nessa imagem, o uso de um listener de eventos unload
torna a página
não qualificada para o bfcache. É possível
corrigir isso mudando de unload
para pagehide
:
window.addEventListener('pagehide', ...);
window.addEventListener('unload', ...);
O Lighthouse 10.0 também adicionou uma auditoria bfcache, que executa um teste semelhante. Para mais informações, consulte os documentos de auditoria bfcache.
Como o bfcache afeta a medição de análise e desempenho
Se você usa uma ferramenta de análise para rastrear as visitas ao site, pode notar uma redução no número total de visualizações de página informadas, já que o Chrome ativa o bfcache para mais usuários.
Na verdade, é provável que você já esteja subregistrando as visualizações de página de outros navegadores que implementam o bfcache, porque a maioria das bibliotecas de análise conhecidas não rastreia restaurações do bfcache como novas visualizações de página.
Para incluir restaurações do bfcache na contagem de visualizações de página, defina listeners para o evento pageshow
e verifique a propriedade persisted
.
O exemplo a seguir mostra como fazer isso com o Google Analytics. Outras ferramentas de análise provavelmente usam uma lógica semelhante:
// Send a pageview when the page is first loaded.
gtag('event', 'page_view');
window.addEventListener('pageshow', (event) => {
// Send another pageview if the page is restored from bfcache.
if (event.persisted) {
gtag('event', 'page_view');
}
});
Meça a proporção de ocorrências do bfcache
Para identificar páginas que ainda não usam o bfcache, meça o tipo de navegação para carregamentos de página da seguinte maneira:
// Send a navigation_type when the page is first loaded.
gtag('event', 'page_view', {
'navigation_type': performance.getEntriesByType('navigation')[0].type;
});
window.addEventListener('pageshow', (event) => {
if (event.persisted) {
// Send another pageview if the page is restored from bfcache.
gtag('event', 'page_view', {
'navigation_type': 'back_forward_cache';
});
}
});
Calcule a proporção de ocorrência do bfcache usando as contagens de navegações back_forward
e back_forward_cache
.
Os motivos para que uma navegação de retorno ou de avanço não use o bfcache incluem o seguinte comportamento do usuário:
- Feche e reinicie o navegador.
- Duplicar uma guia.
- Fechamento e restauração de uma guia.
Em alguns desses casos, o navegador pode preservar o tipo de navegação original
e mostrar um tipo de back_forward
, mesmo que elas não sejam navegações
de retorno ou de avanço. Mesmo quando os tipos de navegação são informados corretamente, o bfcache é
descartado periodicamente para economizar memória.
Por isso, os proprietários de sites não podem esperar uma proporção de hits do bfcache de 100% para todas as navegações de back_forward
. No entanto, medir a proporção pode ajudar a identificar
páginas que impedem o uso do bfcache.
A equipe do Chrome está trabalhando em uma
API NotRestoredReasons
para ajudar a expor os motivos pelos quais as páginas não usam o bfcache, para que os desenvolvedores possam
melhorar as taxas de hit do bfcache.
Medição da performance
O bfcache também pode afetar negativamente as métricas de desempenho coletadas no campo, especificamente as que medem os tempos de carregamento da página.
Como as navegações bfcache restauram uma página atual, em vez de iniciar um novo carregamento de página, o número total de carregamentos de página coletados diminui quando o bfcache está ativado. No entanto, os carregamentos de página que o bfcache substituem provavelmente estão entre os carregamentos de página mais rápidos do seu conjunto de dados. Isso ocorre porque os carregamentos de página repetidos, incluindo navegações de retorno e avanço, geralmente são mais rápidos do que os primeiros carregamentos de página devido ao armazenamento em cache HTTP. Portanto, ativar o bfcache pode fazer com que sua análise mostre um carregamento de página mais lento, apesar de melhorar o desempenho do site para o usuário.
Há algumas maneiras de lidar com esse problema. Uma delas é anotar todas as métricas de carregamento de página com o respectivo tipo de navegação: navigate
, reload
, back_forward
ou prerender
. Isso permite que você continue monitorando seu desempenho nesses tipos de navegação, mesmo que a distribuição geral tenha uma distorção negativa. Recomendamos essa abordagem para métricas de carregamento de página
não centradas no usuário, como Tempo até o primeiro byte (TTFB, na sigla em inglês).
Para métricas centradas no usuário, como as Core Web Vitals, é melhor informar um valor que represente com mais precisão a experiência do usuário.
Impacto nas Core Web Vitals
As Core Web Vitals medem a experiência do usuário em uma página em várias dimensões (velocidade de carregamento, interatividade e estabilidade visual). É importante que as métricas das Core Web Vitals reflitam o fato de que os usuários encontram restaurações do bfcache como navegações mais rápidas do que o carregamento de página padrão.
Ferramentas que coletam e informam sobre as Core Web Vitals, como o Chrome User Experience Report, tratam restaurações do bfcache como visitas separadas à página no conjunto de dados. Embora não haja APIs de desempenho da Web dedicadas para medir essas métricas após as restaurações do bfcache, é possível aproximar os valores usando as APIs da Web atuais:
- Para a Maior exibição de conteúdo (LCP, na sigla em inglês), use o delta entre o
carimbo de data/hora do evento
pageshow
e o do próximo frame pintado, porque todos os elementos nele serão exibidos ao mesmo tempo. No caso de uma restauração bfcache, a LCP e FCP são as mesmas. - Para Interaction to Next Paint (INP), continue usando o Performance Observer atual, mas redefina o valor atual de CLS como 0.
- Para a Mudança de layout cumulativa (CLS), continue usando o Performance Observer atual, mas redefina o valor atual do CLS para 0.
Para mais detalhes sobre como o bfcache afeta cada métrica, consulte as páginas de guias de métricas individuais do Core Web Vitals. Para ver um exemplo específico de como implementar versões do bfcache dessas métricas, consulte PR adicionando-as à biblioteca JS da web-vitals.
Outros recursos
- Armazenamento em cache do Firefox (bfcache no Firefox)
- Cache da página (bfcache no Safari)
- Cache de avanço e retorno: comportamento exposto na Web (diferenças de bfcache nos navegadores)
- Testador de bfcache (teste como diferentes APIs e eventos afetam o bfcache nos navegadores)
- Performance Game Changer: cache de avanço e retorno do navegador (um estudo de caso da Smashing Magazine que mostra melhorias drásticas nas Core Web Vitals com a ativação do bfcache)