Service Workers – Or: How I Accidentally Cached My Mistakes Forever


You deploy a hotfix.
Then reload your site.
And reload.
And reload again… but your changes just won’t show up.
Welcome to the Service Worker Spiral™ – a dev-friendly feature until it becomes your worst caching nightmare.
Let’s break down why service workers can be evil (unintentionally), what they actually do, and how to cache responsibly so you don’t end up with ghost bugs from three commits ago.
💡Wait... What is a Service Worker?
Think of a service worker as your frontend’s little offline butler.
It:
Sits between your app and the network
Caches resources for offline use
Can intercept, modify, or mock network requests
It enables Progressive Web Apps (PWAs) to work offline and load fast, even with flaky internet.
Sounds awesome, right?
Until it caches a broken version of your site and refuses to die.
Symptoms You’ve Been Betrayed by a SW
Your new UI changes don’t show up, no matter how many hard refreshes you do
You delete
.next/
ordist/
folders like a lunatic trying to “reset” itOther people see the correct version, but you don’t
You clear cache, it works — but next reload… 💀 same old bug
🤔 Why Does This Happen?
Because service workers cache aggressively and persist even after you deploy a new version.
By default:
Once installed, the service worker keeps serving its cached assets until it's told to update
The browser doesn’t switch to a new version until a full reload and the old worker is unregistered
TL;DR: You’re viewing a time capsule of your app unless you manage versions properly.
🔥 The Fix: How to Cache Without Getting Cursed
✅ 1. Version Your Cache
In your service worker code, always version your cache:
const CACHE_NAME = "my-app-cache-v2"; // 👈 bump this every deploy
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll([
"/", "/index.html", "/styles.css", "/main.js"
]);
})
);
});
This ensures the browser sees a new cache and replaces the old ones.
✅ 2. Clean Up Old Caches
Old caches stick around like forgotten snacks in your bag.
Add this in activate
:
self.addEventListener("activate", (event) => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then((keyList) =>
Promise.all(
keyList.map((key) => {
if (!cacheWhitelist.includes(key)) {
return caches.delete(key); // 🧹
}
})
)
)
);
});
✅ 3. Use skipWaiting()
and clients.claim()
Service workers usually wait until the next tab reload to activate.
Fix that behavior:
self.addEventListener("install", (event) => {
self.skipWaiting(); // Activate immediately
});
self.addEventListener("activate", (event) => {
clients.claim(); // Take control right now
});
Careful: This can force update the SW on users. Use with good versioning.
✅ 4. Tell Users When an Update is Available
Use something like this in your main JS:
navigator.serviceWorker.addEventListener("controllerchange", () => {
// SW has changed
window.location.reload(); // Refresh to get new version
});
You can even show a “New version available! Refresh?” popup for a better UX.
✅ 5. Use Tools Like Workbox
Google’s Workbox gives you battle-tested defaults for:
Caching strategies
Cache expiration
Auto-updating
Routing fallback for offline
It’s like Tailwind but for service workers — saves time, reduces bugs, and comes with opinions that won’t ruin your app.
Mistakes Devs (and I) Have Made
❌ Mistake | 💡 Fix |
Not versioning cache | Always update CACHE_NAME |
Relying on users to refresh | Use skipWaiting() + notify users |
Not deleting old caches | Clear them in activate |
Not testing with cache enabled | Always test production builds with SW |
Using default create-react-app SW | Replace with your own or Workbox |
🔍 How to Debug Like a Pro
Open DevTools → Application → Service Workers
Check:
Is there an active service worker?
What’s the scope?
Is it controlling the page?
Use “Unregister” button to nuke the SW if testing
Also inspect Caches section to see what’s being stored
Final Thoughts
Service workers are powerful. But with great caching comes great responsibility.
To make your deploys clean and update-friendly:
Version the cache
Force update responsibly
Clean up after yourself
Tell users when updates are ready
Otherwise, your users are stuck in a haunted version of your app from 3 weeks ago — and you’ll be gaslighting yourself like:
“But I swear I fixed that bug already..”
📚 This post is part of It Worked in Dev — your trusted source for chaotic deploys, hidden frontend sins, and how to fix ‘em like a boss.
Want more sicy dev stories?
Follow for more posts on hydration errors, CDN betrayals, and why console.log might just expose your soul 👀
Subscribe to my newsletter
Read articles from Faiaz Khan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Faiaz Khan
Faiaz Khan
Hey! I'm Faiaz — a frontend developer who loves writing clean, efficient, and readable code (and sometimes slightly chaotic code, but only when debugging). This blog is my little corner of the internet where I share what I learn about React, JavaScript, modern web tools, and building better user experiences. When I'm not coding, I'm probably refactoring my to-do list or explaining closures to my cat. Thanks for stopping by — hope you find something useful or mildly entertaining here.