The Invisible Layer: Mastering HTTP Caching (Part 2)
Source: Dev.to
If you’ve ever screamed, “I updated the data in the database, but the user is still seeing the old version!” while frantically hard‑refreshing your browser, you have met the HTTP cache.
When you call fetch('/api/user'), the browser doesn’t immediately go to the internet. It runs through a strict checklist:
- Memory/Disk Check – Do I have a copy of this locally?
- Expiration Check – If yes, is it fresh (based on
max-age)? - Short Circuit – If it is fresh, the browser returns the data immediately and never talks to the server.
These steps are controlled by the Cache‑Control header.
Cache‑Control
The most important header for web performance. It tells the browser exactly how to behave.
max-age (The Timer)
Cache-Control: max-age=3600
The data is fresh for 1 hour (3600 seconds). The browser won’t ask the server again until that time expires.
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)
| Directive | Meaning | Typical Use |
|---|---|---|
no-store | Never save this response. | Sensitive data (e.g., banking info) or data that changes every millisecond. |
no-cache | Save it, but revalidate with the server before using it. | When you need the latest version on every request. |
Note:
no-cacheforces the browser to ask the server, “Is this version still good?” on each request.
Reference: MDN Web Docs – Cache‑Control
ETags and Last‑Modified (304 Not Modified)
When dealing with large resources (e.g., a 2 MB list of 5,000 products), you don’t want to download the whole file every time.
- First request: Server sends the data plus an
ETag(a unique hash/fingerprint). - Subsequent request: Browser sends the
ETagback inIf-None-Match. - Server response: If the hash matches, it returns 304 Not Modified.
Result: The browser reuses the cached version, saving bandwidth and time.
Stale‑While‑Revalidate
Cache-Control: max-age=60, stale-while-revalidate=600
- Fresh (≤ 60 s): Serve instantly from cache.
- Stale (60 s – 600 s): Serve the stale response immediately, then fetch a fresh copy in the background and update the cache.
This eliminates loading spinners while keeping data reasonably fresh.
Deep dive: web.dev – Love your cache (stale‑while‑revalidate)
Why This Matters for React Developers
You might think, “I’m a frontend dev; I don’t configure server headers!”
The reality is you can’t fix with JavaScript what you broke with HTTP.
- If an API sends
Cache-Control: no-store, libraries like React Query (TanStack Query) can’t maintain an effective client‑side cache because the browser refuses to store responses. - If an API sends
max-age=31536000(1 year) for a user profile, users will never see profile updates until the cache expires.
Action: Inspect response headers in Chrome DevTools → Network tab. The browser’s “receipt” system (conditional requests) will let you know when a resource is unchanged (304) and avoid unnecessary downloads.
User Experience Benefits
- Stale‑while‑revalidate shows old data instantly while fetching fresh data in the background, removing the need for loading spinners.
- Proper cache headers reduce latency, bandwidth usage, and improve perceived performance.
Deep Dive Resources
- MDN HTTP Caching Guide – Comprehensive manual on how browsers handle storage.
- Google Web.dev Guide – Practical guide on configuring headers for performance and Lighthouse scores.
- Cloudflare CDN Learning Center – Understanding how edge caching interacts with browser caching.
What’s Next?
Now that we understand the invisible layer, we can move to the Application Layer. In Part 3, we’ll explore React Query (TanStack Query) and see how to implement an efficient caching system with it.
See you in Part 3.