Someone installs your Progressive Web App from the browser. They open it on the train. The article they started yesterday is still there. That feels like magic.
Then they add a product to the cart on patchy Wi-Fi, open checkout from the home-screen icon, and pay against a cached page that still thinks yesterday’s coupon and stock levels are real. Or the service worker serves a stale cart HTML shell while the session cookie and the server disagree. Or they tap Pay with no signal, see a success state that never reached your server, and email you three days later asking where the order is.
Offline is not a checkbox. It is a product decision about which requests may be old, which must be fresh, and which must never pretend to succeed when the network was gone.
This guide is for WordPress sites (especially WooCommerce and other stores) that want a PWA without turning the service worker into a silent checkout bug.
What a service worker actually does on your site
A service worker sits between the browser and the network. Every request can be answered from cache, from the network, or from a mix of both. That is how PWAs keep working in a tunnel. It is also how they serve last Tuesday’s prices if you pick the wrong strategy.
On WordPress, the dangerous part is not “HTML for a blog post.” Blog posts change slowly. A slightly stale article is usually fine. The dangerous part is anything session-shaped or money-shaped:
- Cart fragments and mini-cart markup
- – Checkout and order-pay pages
- – Account, membership, and course progress views
- – Nonces, CSRF tokens, and logged-in admin-bar HTML
- – Prices, stock, and shipping quotes
- – Payment provider scripts that must load from the live origin
If your offline strategy treats those like static marketing pages, you will eventually ship a bug that only appears on mobile data in a parking garage.
Cache-first feels fast. Network-first stays honest.
Progressify’s offline settings (documented under Offline usage) expose the usual Workbox-style strategies. In plain language:
| Strategy | Behavior | Risk on a store |
| — | — | — |
| Network-First | Try the network, cache the result, fall back to cache only if the network fails | Default-safe. Offline still works for pages already seen. |
| Stale While Revalidate | Show cache immediately, update in the background | Fast, but brief staleness is normal. Risky for prices and carts. |
| Cache-First | Prefer cache, network only on miss | Fastest. Most likely to show old content after you publish. |
| Network-Only | Never cache that traffic | Correct for checkout-like flows. No offline for those URLs. |
| Cache-Only | Never hit the network | Almost never what a store wants for HTML. |
Progressify’s docs are blunt: leave the general strategy on **Network-First** unless you have a reason not to. Cache-First and Cache-Only will keep serving old pages after edits until expiration. News, stores with changing prices, and anything time-sensitive should not live there.
That single default prevents most “we turned PWA on and the site lies” support tickets.
What to cache vs what to keep network-first
Think in layers, not “the whole site offline.”
Safe to cache aggressively (with a sane expiration)
- App shell assets: icons, theme CSS/JS that rarely change, splash resources
- – Marketing pages and blog posts the visitor already opened
- – Product gallery images and static category chrome (still refresh with Network-First or SWR, not eternal Cache-Only)
- – Your branded offline fallback page (self-contained: few external fonts, no dependency on uncached scripts)
Keep fresh (Network-First or Network-Only)
/cart,/checkout,/my-account, order-pay, and similar WooCommerce endpoints- – Any URL that embeds nonces or personalized HTML
- – APIs that return stock, shipping, tax, or payment intents
- – Login, logout, and password-reset flows
Offline with a queue, not a fake success
Forms, comments, and some cart actions can be held locally and replayed when the connection returns. That is background sync, not “tell them it paid.” Progressify’s product and docs describe background sync for comments, forms, and WooCommerce cart actions, plus Pro offline form submission that stores locally and sends later.
Read the warning in the docs carefully: do not rely on offline submit for anything that must succeed or fail immediately, such as a payment. The visitor may be told the submission was accepted before your server has seen it. That is fine for a contact form. It is a chargeback waiting to happen for a card capture.
The checkout traps that show up after “PWA installed”
1. Stale cart HTML
The service worker returns a cached cart page. Line items look right. The server session has already changed (stock, coupon, currency). The customer proceeds. The order that lands is not the UI they trusted.
**Mitigation:** Network-First or Network-Only for cart and checkout templates. Short cache expiration if anything cart-adjacent must be cached at all. Test while flipping airplane mode mid-funnel.
2. Offline “success” that never hit the gateway
A custom offline page or a queued request UI that reads like confirmation. The payment provider never charged. Support spends a week untangling it.
**Mitigation:** Checkout and payment routes stay network-dependent. Offline UI must say “you are offline” or “we will retry this action,” never “order placed.”
3. Cached nonces and logged-in shells
A cached page includes a nonce that expired, or shows an account menu for a session that ended. Forms fail mysteriously. People blame WooCommerce.
**Mitigation:** Do not cache personalized responses as if they were public marketing HTML. Exclude account views. Prefer network for authenticated HTML.
4. Precache that is too greedy
Some PWA setups precache huge swaths of the site on install. First install eats data. Old product URLs linger. Editors publish a price change and mobile home-screen users keep seeing the precached copy.
**Mitigation:** Cache what the visitor has actually opened, plus a small shell. Use Network-First so a reconnect updates the copy. Set expiration in days that match how often your catalog moves.
5. Two caching layers fighting
A full-page cache plugin, a host cache, a CDN, and a service worker all invent their own truth. Purges clear one layer and not the others.
**Mitigation:** Know which layer owns HTML for anonymous users (usually the page cache or CDN) and which owns offline resilience (the service worker). When something is stale, unregister the service worker in DevTools and purge the page cache separately so you know which layer lied.
Offline UX that does not feel like a browser error
When a visitor requests a URL that was never cached and they have no connection, they should see your page, not Chrome’s dinosaur.
Progressify Pro lets you point at a custom offline fallback (for example /offline). Docs advice that still holds: make it self-contained. Explain what happened. Offer links to sections they already visited if you can. Do not build that page out of a page-builder stack that needs six uncached scripts to render a headline.
Pro also has a live offline indicator so people understand why a button did nothing. Without it, “broken site” is the default interpretation.
Install prompt timing is part of the same product
Offline strategy fails in public when people install early, hit a bad offline edge case, and never open the app again. Install timing is not a vanity setting.
We already wrote the longer version: [Don’t show the PWA install prompt on the first visit](https://daftplug.com/blog/progressive-web-app-pwa/dont-show-pwa-install-prompt-first-visit/). The short version for stores and content sites:
- Being installable (manifest, service worker, icons, HTTPS) is not the same as asking well
- – Ask after visit count, time on page, or scroll depth, not on first paint
- – Offer banner, popup, overlay, button, or QR depending on the page
- – Honor dismissal for a period you set
- – Use install analytics by device, browser, and country to see if the rule is too shy or too loud
A shopper who has opened two product pages and scrolled a description is a different install candidate than a cold lander who has not decided you are trustworthy. Progressify is built to let you choose that moment instead of outsourcing it to the browser’s default banner.
Content sites vs stores vs membership
**Content / blog / magazine:** Network-First HTML, branded offline page, cache articles people already read, push when new posts go live. Offline is a reading feature. Stale-by-seconds is usually acceptable.
**WooCommerce / commerce:** Same Network-First default, but treat cart, checkout, account, and payment scripts as sacred. Background sync for cart actions is useful. Offline payment confirmation is not. Test the funnel in airplane mode on purpose before you market “works offline.”
**Courses / membership:** Offline for lesson content people already opened can be a retention win. Keep login, entitlement checks, and checkout network-first. Do not cache a “member-only” shell in a way that leaks state across accounts on a shared device.
Progressify as the all-in-one PWA layer
[Progressify](https://daftplug.com/progressify/) is DaftPlug’s Progressive Web App plugin for WordPress. One plugin covers the stack people usually assemble from three products:
- Manifest, service worker, icons, splash, theme colour
- – Install prompts with rules (visit count, time, scroll) and multiple styles
- – Offline cache strategies per the Offline usage settings, expiration, and Pro branded fallback
- – App-like UI: bottom tab bar, dark mode, page transitions, pull to refresh, toasts, skeleton loaders
- – Push from wp-admin (manual or on new content), without a separate per-message vendor fee on the Progressify side
- – Background sync for failed requests when connectivity returns
- – Optional Android and iOS store bundles generated from the same site (Pro)
It is theme-agnostic in the usual WordPress sense and is documented to work with WooCommerce and common stacks. Setup for a basic installable PWA is minutes, not a native rebuild.
If you also want the store listing path, that is the same product’s Pro app-bundle flow, not a second codebase. We walked through that angle in [How do you turn your website into Android and iOS apps without coding](https://daftplug.com/blog/progressive-web-app-pwa/how-do-you-turn-your-website-into-android-and-ios-apps-without-coding/).
For offline specifically, start with Network-First, set a fallback page, enable the offline indicator if you are on Pro, and only then decide whether offline forms or background sync belong on your funnel. Keep payment pages honest.
A practical rollout checklist
- Install Progressify on staging first if you can.
- 2. Fill in name, icons, colours. Confirm the site is installable on Android and that iOS Add to Home Screen instructions make sense for your audience.
- 3. Leave caching on Network-First. Set expiration to match how often your content or catalog changes.
- 4. Create a simple
/offlinepage. Point the Pro fallback at it. - 5. Exclude or network-only the money paths: cart, checkout, account, order-pay.
- 6. Turn on install prompts with a rule that is not visit one. See the [install prompt post](https://daftplug.com/blog/progressive-web-app-pwa/dont-show-pwa-install-prompt-first-visit/).
- 7. Airplane-mode test: open a few articles or products online, go offline, confirm cached views work, confirm checkout does not fake success.
8. Publish a content or price change. Confirm home-screen users see the update after reconnect (Network-First should handle this; Cache-First users will file bugs).
9. Only then enable push automations and app-shell niceties (tab bar, transitions). Those are polish. Offline honesty is correctness.
Pitfalls worth repeating
- Cache-First on a store because “faster” sounded better in a comparison table
- – Precaching the entire sitemap on first install
- – Offline payment UX that looks like confirmation
- – Showing the install prompt on first paint, then blaming PWA when retention is awful
- – Stacking a second PWA plugin or a hand-rolled service worker beside Progressify
- – Forgetting that only pages a visitor has already loaded are available offline (the fallback for never-seen URLs is correct behavior, not a bug)
- – Editors stuck on stale previews: hard reload or unregister the service worker, and prefer Network-First while editing
One membership for PWA plus the rest of the stack
Progressify is free on WordPress.org with Pro features locked rather than removed. Pro is where you get the deeper half called out on the product page (custom offline fallback, richer offline capabilities, app store builds, deeper analytics, and the rest of the paid feature set).
A DaftPlug Pro license unlocks Progressify along with Lightify (performance), Rankify (SEO and GEO), Generatify (AI), and Snapshotify (backups). One membership. New Pro plugins join existing subscriptions. Site-count tiers are on [pricing](https://daftplug.com/pricing/).
If you were about to buy a PWA plugin, a push vendor, and a separate “app builder,” compare that bundle to one license that already includes the PWA layer and the rest of the DaftPlug line.
Bottom line
A WordPress PWA that “works offline” is easy to demo on a blog post. A WordPress PWA that does not break checkout is a discipline: Network-First by default, money paths kept honest, offline queues only where delayed success is acceptable, install prompts after trust, and a branded fallback instead of a browser error page.
[Progressify](https://daftplug.com/progressify/) is the all-in-one layer for that discipline on WordPress. Ship the installable app. Keep the cart on the network. Ask for the home screen when someone already has a reason to care.
