Skip to content
SEWWA

Blog

PWA Service Worker SEO: How Caching Quietly Kills Your Indexing

Sep 19, 2026 — SEO, JavaScript, Web Development, Frontend

PWA Service Worker SEO: How Caching Quietly Kills Your Indexing

Here’s a bug that never throws an error, never shows up in Search Console as a crash, and never triggers a single alert. It just quietly serves Googlebot — and your actual visitors — a version of your page that’s weeks or months old. No 500. No 404. Just a confident 200 with stale content, served by the exact same caching layer that makes your Progressive Web App feel instant.

That’s the trap with service workers. They’re the reason a PWA can load in under a second on a spotty connection, work offline on a train, and feel like a native app without an app store in sight. But the same aggressive caching that earns you those wins can, if you’re not careful, become the thing quietly capping your organic growth. You’ll ship a content update, watch it look perfect in your browser, and never realize repeat visitors and crawlers are still looking at last month’s page.

Why This Keeps Happening in 2026

PWAs aren’t a niche anymore. Over 54,000 sites are running production PWA stacks as of early 2026, and adoption keeps climbing because the pitch is genuinely good — 50 to 70% lower build cost than native apps, instant updates, and engagement lifts that businesses report in the 20 to 250% range after switching. On top of that, 2026’s PWAs are shipping with on-device AI inference, offline-first sync, and access to hardware capabilities (camera, Bluetooth, background sync) that used to be native-app-only territory.

That’s exactly why the SEO risk is bigger now than it was three years ago. Service workers are getting more sophisticated — AI-generated caching strategies that adapt to user behavior, smarter offline fallbacks, more aggressive pre-caching for perceived speed. Every one of those improvements is a UX win and a potential SEO liability if the caching logic doesn’t distinguish between “this asset can live forever” and “this HTML needs to be fresh every single time it’s requested.”

The uncomfortable part is that the failure is invisible by design. A cache-first strategy is working exactly as intended when it serves a cached response instead of hitting the network — that’s the whole point of a service worker. The problem is that “working as intended” and “serving Googlebot outdated content” look identical from the inside. You won’t find this in a crawl error report. You’ll find it when your rankings quietly plateau and you can’t figure out why fresh content isn’t moving the needle.

What Actually Breaks

There are really two failure modes here, and they’re different enough that they need different fixes.

Stale HTML surviving a deploy. This is the headline problem: your service worker caches the HTML document itself using a cache-first strategy, meaning once a page is cached, every subsequent request — from a real user, a repeat visitor, or Googlebot — gets served the cached copy instead of hitting your server. You push a content update, verify it in an incognito window (which bypasses the cache), and ship it. Meanwhile, everyone with an existing service worker installation keeps seeing the old version until their cache naturally expires or gets busted. There’s a documented pattern where content changes appear to Googlebot’s live fetch tool immediately, but repeat crawls and repeat visitors keep seeing weeks-old pages because the cache never got invalidated.

Structured data that never reaches the rendered output. This one’s sneakier. If your service worker intercepts a page request and serves a cached shell before your JSON-LD or dynamic meta tags get injected client-side, the version search engines actually evaluate may never contain that structured data at all. You can have perfectly valid schema in your source code and still fail rich-results eligibility because the cached response search engines see never executed the script that would have added it.

→ Read also: Schema Markup Mistakes That Kill Rich Results and AI Citations in 2026

Neither of these shows up as a technical error. Your Lighthouse score stays great — a well-cached PWA usually scores well on performance. Your uptime stays at 100%. It’s a content-freshness problem hiding inside a performance win, which is exactly why it survives so many QA passes.

The Caching Strategy That Actually Works

The fix isn’t “cache less.” It’s “cache by resource type, not by blanket rule.” Treat your service worker cache the way you’d treat an HTTP cache-control header — different content types earn different freshness guarantees.

Static assets — cache-first, always. Fonts, CSS bundles, JS chunks, and images with versioned or hashed filenames should be cache-first without hesitation. They’re either unchanged (safe to serve from cache indefinitely) or they have a new filename entirely (so the old cache entry is simply irrelevant, not stale). This is where a service worker earns its keep — there’s zero SEO risk in serving a hashed app.a3f9c2.js from cache, because a content change always means a new hash and a cache miss.

Indexable HTML documents — network-first or stale-while-revalidate, never cache-first. Blog posts, product pages, category listings — anything you want Google to crawl and rank — should never be served purely from cache. Network-first means the service worker tries the network, falls back to cache only when offline. Stale-while-revalidate is the middle ground: serve the cached version instantly for speed, but simultaneously fire a background fetch that updates the cache for next time. Either approach keeps your indexable pages fresh while still getting most of the perceived-speed benefit that made you want a service worker in the first place.

Dynamic, personalized, or frequently-updated pages — don’t cache the document at all. Cart pages, account dashboards, anything with per-user state has no business in a shared cache anyway, but it’s worth saying explicitly: exclude these routes from your service worker’s fetch handler entirely rather than trying to tune a caching strategy for content that shouldn’t persist.

Here’s roughly what that split looks like in a sw.js fetch handler:

self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
// Static, versioned assets: cache-first
if (/\.(js|css|woff2?|png|jpg|avif)$/.test(url.pathname) && url.search.includes('v=')) {
event.respondWith(
caches.match(request).then((cached) => cached || fetch(request))
);
return;
}
// Indexable HTML documents: network-first, fall back to cache offline
if (request.mode === 'navigate' || request.destination === 'document') {
event.respondWith(
fetch(request)
.then((response) => {
const clone = response.clone();
caches.open('pages-v1').then((cache) => cache.put(request, clone));
return response;
})
.catch(() => caches.match(request))
);
return;
}
// Everything else: pass through untouched
});

The key line of reasoning to keep in your head: if a crawler seeing a cached version of this URL would be a problem, it can’t be cache-first.

SEO & Search Implications

This is where the PWA conversation and the SEO conversation are really the same conversation, not two adjacent ones. A service worker’s entire job is to intercept the network layer between the browser and your server — and Googlebot’s rendering service, when it executes JavaScript on your page, is subject to that same interception. If your caching strategy doesn’t account for what a crawler needs (fresh HTML, fully-rendered structured data, correct status codes on direct navigation), you’re not making a UX tradeoff — you’re actively degrading discoverability while your Core Web Vitals dashboard looks fantastic.

→ Read also: Core Web Vitals and AI Overview Citations in 2026

There’s a second layer here worth naming: your Web App Manifest and start_url matter more than most teams assume. Both the page URL and the manifest’s start_url need to resolve with a 200 status, and the manifest itself needs complete metadata — icons, theme_color, display mode. An incomplete or broken manifest doesn’t just weaken your install-banner eligibility; it’s one more signal search engines use to evaluate whether your PWA is a legitimate, well-formed app experience versus a thin wrapper. Get the manifest right, and it reinforces the same trust signals your caching strategy is trying to protect.

Common Mistakes and How to Catch Them Early

The single most common mistake is treating “PWA-ready” and “SEO-ready” as the same checklist item, when they’re really evaluated by two different consumers of your page. A user’s browser and Googlebot’s renderer will sometimes disagree about what a given URL currently contains, and if you only test in your own browser — where you’re logged in, cached, and probably ignoring incognito mode half the time — you’ll never catch the drift.

The other mistake worth flagging: an empty-shell fallback route that returns a 200 for every path when offline or uncached. It’s a reasonable UX pattern for actual offline scenarios, but if that fallback ever gets served to a crawler as the primary response for a real URL, you’ve just manufactured an unlimited number of thin, near-duplicate pages — the opposite of what you want in an index.

To catch drift before it costs you rankings:

→ Read also: JavaScript SEO for AI Crawlers: React, Next.js, and Astro in 2026

Conclusion

A service worker is one of the best performance tools available to a modern web app, and there’s no reason to dial back what it does for static assets — cache them aggressively, forever, without guilt. The discipline that actually matters is narrower than “be careful with caching” in the abstract: keep your indexable HTML on network-first or stale-while-revalidate, keep dynamic routes out of the cache entirely, and periodically verify that what a crawler sees matches what a fresh visitor sees. Get that split right, and your PWA keeps every bit of the speed and offline resilience that made it worth building — without quietly capping the organic traffic it was also supposed to help win.

FAQ

Does having a service worker hurt SEO by default? No — a service worker itself is invisible to ranking algorithms. The risk comes specifically from cache-first strategies applied to indexable HTML documents, not from having a service worker at all.

Will Googlebot ever see my offline fallback page instead of real content? It can, if your fetch handler serves the offline fallback for a URL that should resolve normally. Scope offline fallbacks to genuine network-failure cases, not as a catch-all default response.

Is stale-while-revalidate safe for pages I want ranked? Yes, and it’s usually the best balance — it serves the cached copy instantly (good for perceived speed and Core Web Vitals) while updating the cache in the background, so the next request — including a crawler’s next visit — gets fresh content.