Użytkownicy naciskają przycisk wstecz częściej niż prawie każdy inny element sterujący w przeglądarce. Oczekują, że poprzedni ekran pojawi się natychmiast, dokładnie w tym miejscu, w którym go zostawili. Nowoczesne przeglądarki spełniają to oczekiwanie dzięki pamięci podręcznej wstecz/dalej, czyli bfcache. Zamiast niszczyć stronę podczas nawigacji, przeglądarka zamraża ją w pamięci. Po powrocie przywraca migawkę (snapshot). Przeglądarka pomija parsowanie HTML, ponowne wykonywanie JavaScriptu i przeliczanie układu. Wynik wydaje się natychmiastowy, ponieważ strona nigdy w pełni nie „umarła”.

Co tak naprawdę robi bfcache

Zwykłe ładowanie strony jest kosztowne. Przeglądarka musi pobrać zasoby, tokenizować HTML, budować DOM, uruchamiać skrypty, rozwiązywać style, wykonywać układ (layout), malować piksele i składać warstwy. bfcache omija prawie cały ten proces, utrzymując stronę w zamrożonym stanie w pamięci RAM. Nie jest to pamięć podręczna na dysku. Wyrenderowana strona, w tym sterta (heap) JavaScriptu, pozycja przewijania i stan formularza, znajduje się w pamięci, podczas gdy użytkownik czyta kolejną stronę. Gdy użytkownik kliknie „wstecz”, przeglądarka rozmraża migawkę i wywołuje zdarzenie pageshow. Strona wznawia działanie bez kontaktu z siecią i bez ponownego przeliczania układu od zera. Dla użytkowników korzystających z wolnych urządzeń lub niestabilnych połączeń różnica między przywróceniem z bfcache a świeżym ładowaniem może wynosić setki milisekund lub więcej.

Co powoduje błędy

Deweloper przeprowadził niedawno czysty eksperyment, aby dowiedzieć się dokładnie, co blokuje bfcache. Stworzył sześć prostych stron, z których każda testowała jeden podejrzewany czynnik blokujący, a następnie odszedł od nich i nacisnął przycisk wstecz. Wyniki były jasne.

Strona bazowa bez nietypowych nagłówków ani skryptów została przywrócona pomyślnie. Strona ze słuchaczem (listenerem) beforeunload również została przywrócona bez problemów. Co zaskakujące, strona serwowana z Cache-Control: no-store również trafiła do bfcache, co przeczy starszym zaleceniom. Nawet artykuł na blogu na żywo, który mógłby wydawać się zbyt dynamiczny, by go zamrozić, został przywrócony pomyślnie.

Dwie strony zawiodły. Strona ze słuchaczem zdarzenia unload nie mogła zostać przywrócona. Strona z otwartym połączeniem WebSocket również została zablokowana. Te dwa przypadki wskazują na pułapki, które codziennie zastawiają sidła na prawdziwe witryny produkcyjne.

Pułapka zdarzenia unload

Zdarzenie unload od dawna było sygnałem służącym do końcowego sprzątania. Deweloperzy używają go do wysyłania sygnałów analitycznych, zatrzymywania timerów lub czyszczenia tymczasowego stanu. Problem polega na tym, że bfcache opiera się na założeniu, że strona może ponownie „ożyć”. Jeśli przeglądarka wykryje słuchacza unload, zakłada, że strona oczekuje całkowitego zniszczenia i odmawia jej zamrożenia. Nie ma znaczenia, czy dołączona funkcja jest pusta. Sama obecność słuchacza wystarczy, aby zawetować buforowanie we wszystkich nowoczesnych przeglądarkach.

Zamiennikiem jest pagehide. Zdarzenie to jest wywoływane zarówno wtedy, gdy strona jest zamrażana dla bfcache, jak i wtedy, gdy jest faktycznie usuwana. Jeśli musisz rozróżnić te dwa przypadki, właściwość event.persisted przyjmuje wartość true, gdy strona trafia do bfcache. Jednak dla większości zadań związanych z porządkowaniem, pagehide obsługuje oba scenariusze. Przenieś całą logikę sprzątania z unload do pagehide. Następnie całkowicie usuń każdy słuchacz unload, w tym te ukryte w fragmentach kodu analityki firm trzecich lub przestarzałych wtyczkach.

Pułapki aktywnych połączeń

Otwarte połączenie sieciowe lub połączenie z pamięcią masową sygnalizuje, że strona wciąż wykonuje realną pracę. Przeglądarka inwentaryzuje aktywne zasoby w momencie nawigacji. Jeśli znajdzie otwarty WebSocket, aktywne połączenie WebRTC peer lub wiszące połączenie IndexedDB, przerywa zamrażanie i normalnie zamyka stronę. Nie można ufać migawce, dopóki mogą wciąż przepływać bajty danych.

Powinieneś zamykać te zasoby wewnątrz słuchacza pagehide. Wywołaj metodę close swojego WebSocketa. Zamknij połączenia WebRTC peer. Przerwij lub zatwierdź wszelkie oczekujące transakcje IndexedDB. Jeśli Twoja aplikacja potrzebuje tych kanałów po powrocie użytkownika, otwórz je ponownie wewnątrz pageshow. Wzorzec „zamykaj przy pagehide, przywracaj przy pageshow” pozwala stronie kwalifikować się do natychmiastowej nawigacji wstecz bez utraty funkcjonalności.

Niespodzianka z no-store

Przez lata powszechnie uważano, że Cache-Control: no-store uniemożliwia korzystanie z bfcache. Chrome zmienił to zachowanie w 2025 roku. Strona serwowana z no-store może teraz trafić do bfcache. Przeglądarka usuwa zamrożoną migawkę dopiero później, jeśli stany uwierzytelnienia lub pliki cookie zmienią się w sposób unieważniający zapisany stan. Jeśli używałeś no-store jako