by MintJams

爆速「戻る/進む」を諦めない!2025年版 bfcache対応ガイド

bfcacheを阻害するunloadイベントが、2025年に段階的に廃止されます。この記事では、pagehideやvisibilitychangeへの移行方法、bfcacheが効かない原因の特定ツールなど、現代的なWeb開発に必須の対応策を解説します。

Webサイトを「戻る」ボタンで移動したとき、一瞬で前のページに戻れた経験はありませんか?これは bfcache(Back-Forward Cache) のおかげです。

しかし、この bfcache を阻害する悪しき慣習が長く存在しました。その代表が unload イベントです。

Chromeは2025年、このunloadイベントを段階的に廃止する方針を打ち出しました。これは「unloadに依存したサイトは速くならない」という明確なメッセージです。

本記事では、unloadに依存しているウェブサイトを、最新のWeb標準に沿って「bfcache対応」させるための具体的な移行手順を解説します。


この記事でわかること

  • unload イベントの廃止と、なぜ今すぐ対応すべきか
  • unloadpagehidevisibilitychangeに置き換える具体的な方法
  • bfcacheが効かない原因を特定するツールとAPI
  • Cache-Control: no-storeなど、bfcacheに関する最新の知見

なぜunloadイベントは使ってはいけないのか

これまで、ページの終了時に何らかの処理を行うためにunloadイベントが広く使われてきました。しかし、このイベントは信頼性が低く、特にモバイル環境では発火しないことが多々あります。さらに、unloadイベントリスナーが登録されているだけで、bfcacheは完全に無効化されてしまいます。

bfcacheは、ユーザーがブラウザの「戻る」や「進む」ボタンをクリックした際に、ページをメモリにキャッシュしておき、再読み込みなしで瞬時に復元する仕組みです。unloadはこの便利な機能を無効化するため、ユーザー体験を損なう大きな原因となっていました。

Chromeは2025年7月30日(M139)から、unloadを段階的に廃止する計画を進めています。この変更はまず一部のサイトから始まり、最終的にはすべてのサイトでunloadがデフォルトで発火しなくなります。


置き換えのパターン集:unloadを撲滅せよ

unloadの代替として利用できる主要なイベントは以下の3つです。

  • pagehide: ページが非表示になるタイミングで発火。bfcacheにページがキャッシュされる際にも発火します。bfcacheを阻害しません。
  • visibilitychange: ページの表示状態(visiblehiddenprerender)が変化した際に発火。
  • pageshow: ページが再表示されるタイミングで発火。bfcacheから復元された際にも発火します。

これらのイベントを使って、unloadで行っていた処理を安全に移行する方法を見ていきましょう。

1. サーバーへのデータ送信(アナリティクス、ドラフト保存など)

unloadで行っていたサーバーへの最終的なデータ送信(例えば、ユーザーの離脱ログや入力フォームのドラフト保存)は、visibilitychange に置き換えるのが最適です。visibilityStatehiddenになったタイミングで、navigator.sendBeacon()を使ってデータを送信します。

sendBeacon()は、リクエストを非同期で送信し、ページが閉じられてもリクエストがキャンセルされません。

// 画面が見えなくなる最後のタイミングでサーバーへデータを送信
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    // navigator.sendBeacon() を使って安全にデータを送信
    navigator.sendBeacon('/api/log', JSON.stringify({ event: 'page_hidden' }));
  }
});

【この状況で困ったらコレ!】 sendBeacon()が使えない場合は、通常のfetchリクエストにkeepalive: trueオプションを付けることで同様の挙動を実現できます。

fetch('/api/log', {
  method: 'POST',
  body: JSON.stringify({ event: 'page_hidden' }),
  keepalive: true, // ページが閉じてもリクエストが続く
});

2. クリーンアップ処理(タイマー、イベントリスナーの解除など)

ページの終了時に実行していたタイマーの停止や、WebSocketの切断などのクリーンアップ処理は、pagehide を使って安全に行います。pagehideは、unloadと異なりbfcacheを阻害しないため、ページのパフォーマンスを犠牲にすることなく処理を実行できます。

window.addEventListener('pagehide', () => {
  console.log('ページが非表示になりました。bfcacheに保存される可能性あり');
  stopAllIntervals();
  disconnectWebsocket();
});

3. bfcacheからの復元を検知し、状態を再同期

bfcacheからページが復元されたかどうかは、pageshow イベントのpersistedプロパティで判断できます。このプロパティがtrueの場合、bfcache経由で復元されたことを示します。このタイミングで、最新のデータに更新したり、UIを再同期したりする処理を実行します。

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    console.log('bfcacheからページが復元されました!');
    // 必要に応じてAPIを再フェッチするなど、最新の状態に同期する
    refreshData();
  }
});

bfcacheが効かない原因を特定するDevToolsとAPI

せっかく対応しても、何らかの要因でbfcacheが阻害されることがあります。その原因を特定するために、Chrome DevToolsとNotRestoredReasons API(NRR API)を使いましょう。

1. Chrome DevToolsでの検証

Chrome DevToolsを開き、「Application」パネルから「Back/forward cache」を選択します。ここには現在のページがbfcacheの対象になるかどうかが表示され、もし対象外の場合にはその原因がリストアップされます。ワンクリックでテストできるので、開発中の検証に非常に役立ちます。

2. NotRestoredReasons API (NRR API)

JavaScriptからbfcacheの阻害要因を取得できるAPIです。パフォーマンス監視ツールに組み込むことで、本番環境でのbfcacheの適用状況を把握できます。

function logNotRestoredReasons() {
  const nav = performance.getEntriesByType('navigation')[0];
  if (nav?.notRestoredReasons) {
    console.info('bfcacheがブロックされた理由:', nav.notRestoredReasons);
  }
}

// ページの復元時や、ユーザーの操作に応じて実行
window.addEventListener('pageshow', logNotRestoredReasons);

このAPIから取得できる原因の例:

理由 意味
unload-listener unloadイベントリスナーが登録されている
response-cache-control-no-store レスポンスヘッダーにCache-Control: no-storeが含まれている
cache-control-no-store-in-response レスポンスヘッダーにCache-Control: no-storeが含まれている
beforeunload-listener beforeunloadイベントリスナーが登録されている

2025年版の最新情報:Cache-Control: no-storeでもbfcacheが可能に

従来、HTTPレスポンスヘッダーにCache-Control: no-storeが含まれているとbfcacheが無効化されていましたが、Chromeではこの挙動が変更されました。2025年以降、no-storeが付いていてもbfcacheが適用されるようになります。


まとめ:bfcache対応のためのチェックリスト

この記事で解説した内容を、以下のチェックリストで確認しましょう。

  • unloadイベントの使用をゼロにする。
  • 画面が非表示になるタイミングでのデータ送信は visibilitychange を使う。
  • ページのクリーンアップは pagehide を使う。
  • bfcacheからの復元時には pageshow.persisted で状態を再同期する。
  • NRR API と DevTools を活用してbfcacheの阻害要因を継続的に監視する。

ブラウザの「戻る」や「進む」が爆速になることは、ユーザー体験を劇的に向上させます。この移行は、現代のWeb開発者にとって避けては通れない道です。

この記事が、bfcache対応を円滑に進める一助となれば幸いです。ご覧いただきありがとうございました。