Exploring BF Cache and Various Types of Caching

Reza Ghorbani
MCINext
Published in
6 min readMay 19, 2024

--

Exploring BF Cache and Various Types of Caching

Introduction

In our recent project, we encountered a frustrating issue related to user navigation. When users clicked on external links and then attempted to return using the browser’s back button, the entire page would reload or some parts of the page would rerender.
Clearly, something was amiss. The culprit? A lack of proper back/forward cache implementation.

In this article, we’ll explain the caching definition, purposes, benefits, and types classification then explain and analyze a real project challenge with back/forward cache and delve into the reasons behind this issue, explore the architectural choices we made, and discuss the solution that significantly improved user experience.

Caching definition

Caching is a way of saving and retrieving data that can be used to increase computing efficiency. Accessing frequently used information will take less time and resources.
Caching on the server level optimizes server performance while caching on the client side reduces network requests and saves bandwidth.
Bf cache is the client-side browser cache.

Caching Types :

Caching can be classified into three categories:

  • HTTP Caching (server-side and client-side)
  • Client-Side Caching
  • Server-Side Caching

1. HTTP Caching (client and server):

The HTTP cache stores responses associated with requests and reuses them for subsequent requests.
It improves performance by avoiding unnecessary round trips to the server.
HTTP caching includes both client-side caching (browser cache) and server-side caching.
The Cache-Control header can appear in both HTTP responses and HTTP requests. You can configure your server to append the Cache-Control header to the response by specifying which directives to use.

2. Client-Side Caching:

Depends on the user’s browser settings.
This may lead to inconsistent experiences if users clear their cache.

  • Purpose:
    Client-side caching occurs within the user’s browser or client application.
    It stores resources (e.g., images, CSS, JavaScript) locally for faster subsequent visits.
  • Benefits:
    Reduced network requests and faster page loading.
    Enhanced user experience by minimizing data transfer.

Types of client-side caching include:

  1. Browser Cache: Browsers store resources (HTML, CSS, JavaScript, images) locally after the first visit. Subsequent visits load cached content faster.
  2. Local Storage: Allows storing data (e.g., user preferences) on the client side.
  3. Session Storage: Similar to local storage but limited to a session (cleared when the session ends).
  4. Cookies: Used for small data storage and session management.
  5. IndexedDB: A more advanced client-side database for storing structured data.
    In terms of bfcache, IndexedDB connections must be closed so bfcache works
  6. Back/Forward Cache
  • Bfcache is a feature that allows browsers to create and store a snapshot of an already visited web page in their in-memory. So the next time a visitor navigates back or forward to it, the browser can display it immediately.
    Ensures a smoother user experience during navigation.
  • How is the back/forward cache different from the HTTP cache?
    The browser HTTP cache stores past responses to network requests to avoid having to redownload resources.
    The back/forward cache is more comprehensive: the entire page state can be restored.
    When only the HTTP cache is used some resources may still have to be redownloaded if they are not eligible for caching. After that, the page still needs to run any on-page scripts and render the page contents. With the back/forward cache, much of this work can be avoided.
  • When is a page served from the back/forward cache?
    To benefit from the back/forward cache, a page must be eligible. Here are some common reasons why a page might not be eligible:
    It uses the unload event (as restoring the page after unload has been handled may break the page)
    It uses the beforeunload event (in Firefox)
    It uses the Cache-Control: no-store header that disables all caches
    It uses the IndexedDB open connections (connections must be closed)
  • Test to ensure your pages are cacheable
    Navigate to the page in Chrome.
    In DevTools, go to Application > Back-forward Cache.
    Click the Run Test button. DevTools then tries to navigate away and back to determine whether the page can be restored from bfcache.

3. Server-Side Caching:

Requires server resources for caching.
Ensures consistency across users.

  • Purpose:
    Server-side caching involves storing responses on the server before delivering content to clients.
    It reduces the load on the server and improves overall performance.
  • Benefits:
    Reduced network requests and faster page loading.
    Enhanced user experience by minimizing data transfer.

Types of server-side caching include:

  1. Page Caching: Stores entire HTML pages as static files. Commonly used for static websites.
  2. Object Caching: Caches database queries or API responses. Popular tools include Redis and Memcached.
  3. Opcode Caching: OPcache is a type of caching system that saves precompiled script bytecode in a server’s memory called a cache, so each time a user visits a web page, it loads faster.
  4. Reverse Proxy Caching: Uses a proxy server (e.g., Varnish) to cache responses from the origin server.
  5. Content Delivery Network (CDN) Caching: CDNs distribute cached content across multiple servers globally.
    Cached copies of static assets (images, CSS, JS) are stored on edge servers close to users.
    Reduces latency and improves load times for users in different regions.
  6. Database Caching: Database caching stores frequently accessed database queries or results in memory.
    Helps reduce database load and speeds up data retrieval.
    Common tools include Redis, Memcached, and database query caching.

The Problem: Missing Back/Forward Cache

Architecture Overview

  1. module federation: Our project followed the module federation architecture. In this setup, different parts of the web page are rendered and fetched client-side, along with runtime integration. While this approach offers flexibility, it introduces complexities related to caching.
  2. server-side rendered HTML pages: In this architecture web pages render server-side and the browser will cache the whole page for back/forward behavior but as mentioned there are some considerations to have a better user experience that will be explained below.

Main reasons for missing Back/Forward Cache

  1. module federation:
  • Version Conflicts and Hash String: The heart of the problem lies in conflicts arising from loading different versions of remotes in the host. When an updated version clashes with a cached version in the user.
  • Rendering client-side page with javascript: The pre-requisite of bf cache is HTML pages with as little as possible JS but in this architecture, all the rendering process in the browser is javascript base and the browser can not cache and save this rendered page by default.

2. Server-side rendered HTML pages:

  • client-side components:
    In these kinds of pages if all parts of the page are rendered on the server by considering the bf cache rules like avoiding no-store cache the user will have a smooth experience, but if some components of the HTML page are being rendered client-side with javascript most times need to fetch some data from the client to render that part of the page, in the back/forward navigation the whole page will show instantly but these client-side parts of the page will be rendered each time and not catchable.
    client-side fetch tells the browser not to cache this part of the page because it’s not ready to be saved in the browser like no-store cache.
  • client-side onChange values: if you want to keep the state of the component, another great solution is to use ref to sync the state of the input element with the state of the component.
    we can also save use interaction data in local storage or cookies.
const PageAbc = ({ label, url }) => {
const [checkedMapping, setCheckedMapping] = useState({});
const inputRefs = useRef([]);

useEffect(() => {
// This useEffect will run when user navigate back
// it will sync the input state that cached by browser with the state of the component
const updateMapping = inputRefs.current.reduce((acc, ref) => {
acc[ref.value] = ref.checked;
return acc;
}, {});
setCheckedMapping(updateMapping);
}, [])

return (
<div>
{DATA.map((item, index) => (
<div key={item.value}>
<input
type="checkbox"
// store ref value to sync with checkedMapping later
ref={ref => inputRefs.current[index] = ref}
value={item.value}
checked={checkedMapping[item.value]}
onChange={() => {
setCheckedMapping((prev) => ({
...prev,
[item.value]: !prev[item.value],
}));
}}
/>

<label htmlFor={item.value}>{item.label}</label>

<a href={item.url} target="_blank" rel="noopener noreferrer">
View detail
</a>
</div>
))}

<button disabled={Object.values(checkedMapping).every(Boolean)}>
Continue
</button>
</div>
);
};

https://davidtran.dev/blogs/back-and-forward-cache-in-react

Conclusion

After reading this article, you should understand the different types of caching strategies and use cases.
Consider your application’s requirements, scalability, and performance goals before choosing a caching strategy.

References :
https://www.debugbear.com/blog/back-forward-cache https://web.dev/articles/bfcache

By the way, if this article helped you, don’t forget to clap & subscribe! 🙂

thanks.

--

--