Progressive Web App (PWA)

No Comments
Progressive web app (pwa)
AI Summary

A Progressive Web App adds service workers, a manifest, and offline caching on top of a website so it behaves like an installed app. It is still a website, so the SEO game is keeping your content in crawlable HTML instead of trapping it behind app shell JavaScript.

  • The main failure mode is a service worker that caches an empty app shell, so crawlers and stale users see no content.
  • Use a network first strategy for HTML navigations and treat the cache as an offline fallback.
  • Googlebot largely ignores service workers when rendering, so the fetched HTML must carry your content on its own.
  • Serve real, unique URLs rather than hash fragments so every view is crawlable.
Pwa service worker decision diagram contrasting caching an empty app shell with a network first strategy that serves real html.
A network first service worker serves real content for navigations and keeps the cache as an offline fallback.

Progressive Web App (PWA): definition and why it matters

A Progressive Web App is a website built with service workers, a web app manifest, and offline caching so it behaves like an installed app: it can load without a network, work offline, and get pinned to a home screen. The SEO stakes are simple: a PWA is still a website, and Google ranks the HTML it can crawl, so the whole game is making sure your content lives in crawlable HTML and isn't trapped behind app-shell JavaScript.

Done right, a PWA is fast and indexable. Done wrong, it ships an empty app shell to Googlebot, the service worker caches a login or loading screen, and your content never makes it into the index. The failure mode looks a lot like the one that hits any Single Page Application.

The one thing that breaks PWA SEO: caching the wrong shell

Service workers intercept requests and serve from cache. That's the superpower and the footgun. If your worker caches an app shell that contains no real content, and your content only appears after client-side JavaScript fetches it, then a crawler (or a returning user on a stale cache) sees an empty frame.

// BAD: app-shell-only strategy that can starve crawlers of content
self.addEventListener('fetch', (event) => {
  // Every navigation returns the empty shell; content is fetched later by JS.
  event.respondWith(caches.match('/app-shell.html'));
});

// BETTER: network-first for HTML documents, cache as offline fallback
self.addEventListener('fetch', (event) => {
  if (event.request.mode === 'navigate') {
    event.respondWith(
      fetch(event.request)
        .then((res) => {
          const copy = res.clone();
          caches.open('pages').then((c) => c.put(event.request, copy));
          return res;                      // real, content-filled HTML
        })
        .catch(() => caches.match(event.request) || caches.match('/offline.html'))
    );
    return;
  }
  // static assets: cache-first is fine
  event.respondWith(caches.match(event.request).then((r) => r || fetch(event.request)));
});

The rule: serve real content-bearing HTML for navigations, and use the cache as an offline fallback, not as the default response. Googlebot largely ignores service workers when rendering anyway, so the page it fetches must contain your content on its own.

PWA features vs. their SEO impact

PWA featureWhat it doesSEO riskMitigation
Service workerIntercepts requests, enables offlineCan cache an empty shell / stale contentNetwork-first for HTML; content in initial response
App shell architectureLoads a UI frame, fills content via JSCrawler sees empty frame if JS-dependentRender content server-side or prerender
Web app manifestEnables install + home-screen iconNone (metadata only)Just keep it valid
Offline cachingServes cached pages with no networkServing outdated content to usersVersion your cache; expire old entries
Client-side routingNavigates without full reloadsSame as SPA: thin initial HTML, history API issuesSSR / prerender; real URLs, not # fragments

How to check your PWA's crawlability

  1. View the raw HTML, not the rendered page. Run curl -A "Googlebot" https://yoursite.com/page or use "View Source." If your main content isn't in that HTML, crawlers may not see it.
  2. Use the URL Inspection tool. In Search Console, inspect a live URL and view the rendered HTML and screenshot. Confirm your actual content, not a loading spinner, appears.
  3. Test the service worker's effect. In Chrome DevTools > Application > Service Workers, tick "Bypass for network," reload, and confirm the page still shows content. Then test offline mode for the reverse.
  4. Check that URLs are real and unique. Every indexable view needs its own crawlable URL with a proper server response, not a fragment after #.
  5. Confirm the manifest and worker don't block resources. Make sure robots.txt isn't disallowing the JS/CSS the page needs to render.

Common mistakes and how to fix them

  • Caching the app shell as the default navigation response. Crawlers and stale users get an empty frame. Fix: network-first for HTML documents; cache only as offline fallback.
  • Assuming Googlebot runs your service worker. It generally doesn't during rendering. Fix: the fetched HTML must contain content without the worker's help.
  • Content only appearing after a client-side fetch. That's the classic SPA trap wearing a PWA badge. Fix: server-render or prerender the content-bearing HTML.
  • Fragment-based routing (/#/products). Hash routes aren't distinct URLs for crawlers. Fix: use the History API with real paths.
  • Serving stale cached pages forever. Users and crawlers see outdated content. Fix: version your caches and expire old entries on activate.

FAQ

Are PWAs bad for SEO?

No, not inherently. A PWA that server-renders its content is fast and fully indexable. PWAs get a bad reputation only when they ship an empty app shell and rely on client-side JavaScript to load everything. The architecture is fine; the shell-only pattern is the problem. Our SEO for Progressive Web Apps guide has the checklist.

Does Googlebot execute service workers?

Generally no. Google renders pages in a way that largely ignores the service worker, so your page must return real content in its HTTP response. Don't rely on the worker to inject content for crawlers.

Do I still need SSR if I build a PWA?

If your content is generated client-side, yes, you need server-side rendering or prerendering so the initial HTML carries the content. A PWA layered on top of server-rendered pages gets you both offline capability and clean indexing. This is the same requirement that applies to any SPA.

Does the web app manifest affect rankings?

No. The manifest controls install behavior, icons, and theme color. It's metadata for the browser, not a ranking signal. Keep it valid, but it won't move your rankings by itself.

How is a PWA different from an SPA?

An SPA is an app architecture (client-side routing, one HTML shell). A PWA adds offline capability, installability, and a service worker on top, and it can be built on an SPA or on server-rendered pages. Many PWAs are SPAs, so they inherit the same empty-initial-HTML risk.

Claude Vincent is a technical SEO consultant focused on crawlability, rendering, and AI-search visibility. He writes the field guides and case studies at SEO ProCheck, with a bias toward the durable, unglamorous work that decides whether search engines and AI answer engines can actually read and cite a site.

About SEO ProCheck

Technical SEO consulting and GEO strategy with 20 years of enterprise experience. Case studies, resources, and tools for search and AI visibility.

Work With Me

Technical SEO audits, GEO strategy, site migrations, and international SEO. Hourly consulting for teams who need hands-on support, not just reports.

Subscribe to our newsletter!

More from our blog