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

Webサイトを「戻る」ボタンで移動したとき、一瞬で前のページに戻れた経験はありませんか?これは bfcache(Back-Forward Cache) のおかげです。
しかし、この bfcache を阻害する悪しき慣習が長く存在しました。その代表が unload
イベントです。
Chromeは2025年、このunload
イベントを段階的に廃止する方針を打ち出しました。これは「unload
に依存したサイトは速くならない」という明確なメッセージです。
本記事では、unload
に依存しているウェブサイトを、最新のWeb標準に沿って「bfcache対応」させるための具体的な移行手順を解説します。
この記事でわかること
unload
イベントの廃止と、なぜ今すぐ対応すべきかunload
をpagehide
やvisibilitychange
に置き換える具体的な方法- 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
: ページの表示状態(visible
、hidden
、prerender
)が変化した際に発火。pageshow
: ページが再表示されるタイミングで発火。bfcacheから復元された際にも発火します。
これらのイベントを使って、unload
で行っていた処理を安全に移行する方法を見ていきましょう。
1. サーバーへのデータ送信(アナリティクス、ドラフト保存など)
unload
で行っていたサーバーへの最終的なデータ送信(例えば、ユーザーの離脱ログや入力フォームのドラフト保存)は、visibilitychange
に置き換えるのが最適です。visibilityState
がhidden
になったタイミングで、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対応を円滑に進める一助となれば幸いです。ご覧いただきありがとうございました。