Web Caching: The guide to finally stop saying "It works on my machine"
Understanding and mastering web caching has never been more critical. From performance and SEO to full control over the user experience, a solid caching strategy can make all the difference, or break everything.
Picture this: you've just fixed a critical bug and deployed the update. Everything looks fine… but one user still sees the broken version. Worse: Google indexes the outdated page. Sound familiar? This is often the result of poor web cache management.
Caching is the web’s short-term memory. It speeds up page loads, reduces server load, and improves user experience. But mismanaged caching can turn into a nightmare: outdated content, SEO issues, persistent bugs.
In this article, we’ll break down the different types of cache (client-side, server-side, HTTP headers, service workers), how to handle invalidation, and what best practices to apply in 2025. Our goal: help you gain speed without losing control. Let’s dive in.
The Different Types of Cache: Who Does What?
Browser Cache (Client-Side)
This is the first layer, controlled via HTTP headers like Cache-Control
, Expires
, or ETag
. It stores files (HTML, CSS, JS, images) in the user’s browser so they don’t have to be downloaded again on every visit.
Example (the browser caches the resource for 1 day):
Cache-Control: public, max-age=86400
Pro Tip: Use versioning in URLs (style.v2.css
) to force re-downloads when needed.
Proxy or Reverse Proxy Cache (Server-Side)
Tools like Varnish, NGINX, or CDNs (Cloudflare, Fastly, etc.) act as intermediaries between the user and your application server. They cache entire HTML pages or static assets.
Their job: respond to requests without touching your backend.
RAM vs. Disk: Two Types of Server Cache
If you manage your own infrastructure, you’ll need to understand how your cache is stored:
- RAM caching stores data in memory. It’s lightning-fast but constrained by available memory
- Disk caching writes files to disk, slower, but great for storing large volumes
Example: A 1MB background image doesn’t need to sit in RAM. A homepage requested 10 times per second might.
Cache Invalidation: The Real Challenge
Caching is powerful… until it serves outdated content. Invalidating a resource (removing it from the cache or forcing an update) is often the hardest part.
TTL and max-age
Most caches expire after a set duration:
Cache-Control: max-age=3600
The resource is considered valid for 1 hour. But what if you need an immediate update?
PURGE or BAN (Varnish, Fastly, etc.)
Advanced proxies let you manually purge cached content:
PURGE /my-page.html HTTP/1.1
Even better: use logical keys like xkey
to invalidate multiple resources at once.
What’s xkey
?
It’s an identifier you attach to related content. For example, every page linked to an article (xkey: article-123
) can be invalidated with a single command.
X-Cache-Tags: article-123, category-news
Bonus: avoid full cache wipes, and keep unrelated content warm.
The Problem with Browser Cache
You can’t manually clear the cache in users’ browsers.
The common solution: cache busting with versioned or hashed file names:
<script src="/js/app.48a9c8b.js"></script>
Changing the file name forces the browser to download the new version.
Service Workers: A Local Proxy Inside the Browser
With the rise of Progressive Web Apps (PWA), service workers are now key players in cache management.
They’re JavaScript scripts that intercept network requests and:
- Serve files from a local cache (via the Cache API)
- Define custom caching strategies: cache first, network first, stale-while-revalidate, etc.
Example:
event.respondWith(
caches.match(request).then(cached => cached || fetch(request))
);
Warning: Service worker caches persist even after a browser refresh. You must update them manually with each deployment.
The Vary Header: Subtle but Critical
The Vary header tells caches what to vary the response by. It’s basically saying, “This response changes depending on X.”
Common cases
Vary: Accept-Encoding
One GZIP version, one Brotli version.
Vary: User-Agent
One version for mobile, another for desktop.
Potential Issues
- SEO: If used incorrectly,
Vary: User-Agent
can block Googlebot from indexing the correct version of your pages. - Security:
Vary: Cookie
can generate one cached version per user session, not only inefficient but potentially dangerous.
Best practice: Use Vary sparingly, and test it with curl -I
or tools like WebPageTest.
Best Practices for 2025
Define a clear cache strategy
- JS/CSS/images: use long-lived caching (max-age=31536000, immutable) and versioned file names
- HTML pages: short TTLs or disable caching altogether (no-store, must-revalidate)
- APIs: implement ETag or Last-Modified
Control intermediate caches
- Configure CDN or reverse proxy TTLs based on content type
- Use PURGE, BAN, or xkey to invalidate only what needs to be refreshed
Automate Invalidation
On each deployment, clear cache entries for modified content.
For APIs? Use conditional headers:
ETag: "v1.3.5"
What’s an ETag
?
An ETag
(Entity Tag) is a unique signature of your content (often a hash). The browser can send:
If-None-Match: "v1.3.5"
If the content hasn’t changed, the server responds with 304 Not Modified, saving bandwidth and load time.
Monitor what’s actually cached
- Use browser DevTools (Network tab -> check “Size” or “From Cache” columns)
- Test with curl, Lighthouse, or WebPageTest
Manage service workers safely
- Update your
service-worker.js
filename on every release to force new installs - Clean up old caches during the
activate
event
Master the Cache, Master the Speed
Caching isn’t just a nice-to-have, it’s a cornerstone of modern web performance. But speed without control leads to stale pages, bugs, and frustrated users.
In 2025, your caching strategy should be:
- Clear
- Tested
- Automated
- Monitored
Because “it works on my machine” has never been a reliable benchmark. But with the right cache setup, it can work, for everyone.