DEV Community

Cover image for The Invisible Layer: Mastering HTTP Caching (Part 2)
Peter Ogbonna
Peter Ogbonna

Posted on

The Invisible Layer: Mastering HTTP Caching (Part 2)

"I updated the data in the database, but the user is still seeing the old version!"

Confused dude

If you've ever screamed this while frantically hard-refreshing your browser, you have met the HTTP Cache.

In Part 1, I discussed the mindset of caching. Today, we look at the mechanics.

Most frontend engineers don't realize the massive conversation happening in the Network Tab. Conversations in the Response Headers determines whether your request even reaches the server at all.

This is why it's important to master the Invisible Layer to understand caching better and know what happens under the hood, most of the library you use are built on this.

The Browser’s Decision Tree

When you call fetch('/api/user') for example, the browser doesn't immediately go to the internet, it goes through a strict checklist.

  1. Memory/Disk Check: Do I have a copy of this locally?

  2. Expiration Check: If yes, is it fresh (based on max-age)?

  3. The Short Circuit: If it is fresh, the browser returns the data immediately. It never talks to the server.

This behavior is controlled by the Cache-Control header.


Now these 3 concepts will help you understand how Caching works on the Network (HTTP) layer

1. The Rules: Cache-Control

This is the most important header in web performance. It tells the browser exactly how to behave.

max-age (The Timer)

Cache-Control: max-age=3600
Enter fullscreen mode Exit fullscreen mode

This tells the browser that "This data is good for 1 hour (3600 seconds). Do not ask the server for this data again until that time is up."

The Trap: If you deploy a critical bug fix 5 minutes later, users with the cached version won't see it for another 55 minutes.

no-cache vs no-store (The Great Confusion)

This is the most common interview question and production mistake regarding caching.

  • no-store says "Never save this."

Use this for sensitive data (banking info) or data that changes every millisecond.

  • no-cache says "Go ahead and save it, but you must check with the server before using it."

Now this forces the browser to ask the server, "Is this version still good?" every single time.

Read more: MDN Web Docs: Cache-Control

2. The Receipt System: ETags and Last-modified (304)

Imagine you have a large list of 5,000 products. You don't want to download that 2MB file every time, but you also need to know if it changed.

This is where Conditional Requests come in.

  1. For the First request, server sends the data + an ETag (a unique hash/fingerprint of the file).

  2. For the Second request, The browser sends that ETag back in a header called If-None-Match.

  3. From the Server’s response, If the hash is the same, the server sends a 304 Not Modified.

The Win: You saved the user from downloading 2MB. The browser just reuses the version it already had.

3. The "Stale-While-Revalidate" Strategy

If you learn one thing from this article, let it be this. It is the gold standard for modern web performance.

Cache-Control: max-age=60, stale-while-revalidate=600
Enter fullscreen mode Exit fullscreen mode

This tells the browser:

If the data is less than 60 seconds old, show it instantly (Fresh).

If it's between 60 seconds and 600 seconds old, show the old data immediately, but in the background, fetch the new data and update the cache for next time (Stale-while-revalidate).

This eliminates the "loading spinner" entirely while still keeping the data relatively fresh.

Deep Dive: web.dev: Love your cache (stale-while-revalidate)

Why This Matters for React Developers

You might be thinking, "I'm a frontend dev, I don't configure server headers!"

Here is the truth, You can't fix with JavaScript what you broke with HTTP.

If your API sends Cache-Control: no-store, your fancy React Query setup will struggle to maintain a cache effectively because the browser is fighting against it. If your API sends max-age=31536000 (1 year) for a user profile, your users will never see their profile updates.

You need to check these headers in the Chrome DevTools Network Tab.

The 3 Key Takeaways:

  1. The Browser is Smart; it tries to save you work by storing files locally, so you need to know how to tell it when to stop using those files.

  2. The "Receipt" System: Instead of downloading a whole file, the browser can just ask the server, "Has this changed?" If the server says "No" (304 Not Modified), you save time and data.

  3. User Experience: Using stale-while-revalidate allows your app to show old data instantly while it fetches new data in the background, eliminating need for loading spinners.

Deep Dive Resources

If you want to become a true expert on this layer, I highly recommend reading these specifications:

  1. MDN HTTP Caching Guide: The comprehensive manual on how browsers handle storage.
  2. Google's Web.dev Guide: A practical guide on configuring headers for performance (and Lighthouse scores).
  3. Cloudflare's CDN Learning Center: Excellent for understanding how Edge caching (CDNs) interacts with browser caching.

What’s Next?

Now that we understand the Invisible Layer, we can finally move to the Application Layer.

In Part 3, we will be looking at React Query (TanStack Query), to see how to implement an efficient caching system with it.

See you in Part 3.

Top comments (0)