Skip to main content

Design Load Tests for Progressive Web Apps 2026

Design Load Tests for Progressive Web Apps 2026

Key takeaways

  • A load test sends HTTP requests, so it never runs your service worker. It measures what your servers do when the service worker cannot answer: first visits, expired caches, API calls and updates.
  • Test those paths on purpose: a cold-cache first visit, the API calls the app makes after load, the burst when many offline users reconnect and replay queued work, and the sending side of push notifications.
  • Check offline behaviour, installability and cache rules in a browser (Chrome DevTools), not with a load tool. A load test cannot tell you whether the app works offline.

Load testing a progressive web app (PWA) means testing the server-side traffic a PWA creates once its service worker is bypassed, expired or updating, plus the bursts that come from reconnecting devices. The part that runs on the phone (offline screens, install prompt, cached shell) is a browser test, not a load test. The rest of the architecture matches a single page application, which the guide to load testing single page applications already covers (API calls, journeys, browser metrics), so this page only adds what is specific to PWAs.

An earlier version of this page was a long checklist with unsourced claims and diagrams with garbled labels. It was replaced with the parts that can be checked against browser documentation.

What makes a PWA different for load testing?

A PWA is a web app with a web app manifest (enough information for the browser to install it) and, usually, a service worker for at least a minimal offline experience; the service worker can also handle push messages (MDN, October 2026). Three consequences matter for a test:

  • Some requests never reach your servers. The service worker intercepts fetch events for every resource it controls and can answer from the Cache API (MDN, October 2026). Repeat visits may cost you almost nothing.
  • Some requests arrive in bursts you did not schedule. Queued work replays when connectivity returns, and a push campaign wakes many devices at once.
  • Some behaviour exists only in the browser. Install state, offline fallback pages and cache versioning are client-side, so no HTTP-level script observes them.

Does the service worker change what a load test measures?

Yes. A protocol-level tool (JMeter, k6, Locust) requests URLs straight from the network, so every virtual user behaves like a first visit with an empty cache. That is the right model for the worst case: new visitors, a cache cleared by a deploy, or users whose cache was evicted. It overstates load for returning users whose service worker answers from cache, and it says nothing about how fast their app feels.

So split your user mix into cohorts and weight them from analytics (new vs returning, installed vs browser tab):

CohortWhat reaches your serverHow to test it
First visit, cold cacheHTML, manifest, service worker script, app shell assets, first API callsProtocol-level script, full asset list
Returning, shell cachedMostly API calls; a re-check of the service worker script on loadScript only the API calls; add the service worker script request
Returning, cache from an older versionNew service worker and new assets fetched againSpike of first-visit traffic right after a deploy
Reconnecting after offlineQueued writes replayed togetherArrival-rate burst against the write endpoints

The new-version row matters most. A new service worker version installs in the background and takes over only when no page uses the old one, with a new cache name recommended so the old cache stays intact (MDN, October 2026). During a release both versions’ assets can be requested, so test a deploy day like a spike, using the spike testing guide, and read cache invalidation under load for the server side of it.

How do you test a cold-cache first visit?

Script what a fresh browser fetches: the HTML, the manifest, the service worker script, the shell assets the service worker precaches, then the API calls the first screen makes. The Cache API’s addAll() is all-or-nothing, so one failed asset download leaves the cache unmodified (web.dev, October 2026); asset errors under load therefore show up as users stuck without offline support. Count them. The k6 sketch below uses placeholder URLs and has not been run against a real app:

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  scenarios: {
    first_visits: {
      executor: 'ramping-arrival-rate',
      startRate: 5,
      timeUnit: '1s',
      preAllocatedVUs: 100,
      maxVUs: 500,
      stages: [
        { target: 50, duration: '3m' },
        { target: 50, duration: '5m' },
        { target: 0, duration: '1m' },
      ],
    },
  },
  thresholds: {
    http_req_failed: ['rate<0.01'],
    'http_req_duration{kind:shell}': ['p(95)<800'],
    'http_req_duration{kind:api}': ['p(95)<500'],
  },
};

const BASE = 'https://app.example.com';

export default function () {
  const shell = http.batch([
    ['GET', `${BASE}/`, null, { tags: { kind: 'shell' } }],
    ['GET', `${BASE}/manifest.webmanifest`, null, { tags: { kind: 'shell' } }],
    ['GET', `${BASE}/sw.js`, null, { tags: { kind: 'shell' } }],
    ['GET', `${BASE}/assets/app.js`, null, { tags: { kind: 'shell' } }],
  ]);
  check(shell[0], { 'shell 200': (r) => r.status === 200 });

  const api = http.get(`${BASE}/api/feed`, { tags: { kind: 'api' } });
  check(api, { 'api 200': (r) => r.status === 200 });
  sleep(Math.random() * 3 + 1);
}

An open model (ramping-arrival-rate) fits here because new visitors arrive on their own schedule and do not wait for earlier ones to finish. Sizing the rate from concurrent sessions is covered in how to calculate concurrent users.

How do you load test offline sync and reconnects?

Background Sync lets a service worker make a network request as soon as the device has connectivity, for example to send a message composed offline (MDN, October 2026). MDN marks it Limited availability, meaning some widely used browsers do not support it, so do not assume every user replays automatically; apps often also replay on the next page open.

From the server’s side, the signal is a burst: thousands of queued writes arriving close together after a network outage, a train leaving a tunnel, or a mobile carrier recovering. Test it as an arrival-rate spike against the write endpoints, and check three things that a steady load hides:

  • Idempotency. A replay can deliver a request twice (the device never saw the first response). Send the same request with the same key twice and confirm only one record results.
  • Ordering and conflicts. Offline edits made hours earlier can conflict with newer server data. Include stale-version writes in the data.
  • Rate limits and queues. A burst from one carrier or region can hit per-IP limits that normal traffic never touches.

How do you load test push notifications?

Push is mostly a sending problem. The browser subscribes through the Push API and your app server stores the subscription; to send a message your server posts to the subscription’s endpoint, and the push service operated by the browser vendor relays it to the device, where the service worker’s push event fires (MDN, October 2026). Your load test cannot generate the device side, and you should not send test load at a vendor’s push service. What you can test is your side:

  • the job that fans a campaign out to many stored subscriptions (queue depth, worker throughput, time to finish a send);
  • the subscribe endpoint and the cleanup of expired subscriptions, which spike after a large send;
  • the traffic that comes back: users tapping a notification all open the app within a short window, a spike of first or returning visits.

What can a load test not tell you about a PWA?

  • Whether the app works offline, shows the right fallback page or installs. Use Chrome DevTools’ Application panel. Lighthouse’s PWA testing is deprecated (developer.chrome.com, October 2026), so do not depend on a PWA score.
  • How fast the cached app feels on a slow phone. Measure that with real-device tests or real user monitoring of Core Web Vitals, and run a few browser-level journeys as described in the SPA guide.
  • Cache storage limits on a user’s device. Quota and eviction apply per origin, and opaque cross-origin responses can be counted at a larger size than they have (web.dev, October 2026), so cache only what the first screen needs.

How does LoadFocus fit in?

LoadFocus runs k6 and JMeter scripts from 25 cloud regions, so the scripts above can run from where your users are, and it can sample Core Web Vitals (LCP, CLS, FCP, TTFB) while virtual users ramp up (loadfocus.com/load-testing, October 2026). Pick the percentile you gate on with the help of why percentiles beat averages.

Frequently Asked Questions

Can I use traditional load testing tools for progressive web apps?

Yes, for the server side. JMeter, k6 and Locust generate the HTTP traffic your servers see. They do not run the service worker, so they model a user with an empty cache. Use a browser for offline and install behaviour.

Does caching make PWA load tests inaccurate?

It makes them conservative for returning users, because the service worker may answer without contacting you. Weight your test by cohort (new, returning, post-deploy) instead of simulating one kind of user.

How do I test offline mode?

In a browser. Chrome DevTools can simulate offline in the Network panel and shows the service worker and caches in the Application panel. A load test only covers what happens when users come back online.

Do push notifications need load testing?

Test your sending pipeline and the traffic spike after a campaign. Do not load the browser vendors’ push services, and do not expect a load tool to emulate devices receiving messages.

How many virtual users should I use?

Start from analytics: peak concurrent sessions split by new and returning users, plus a post-deploy surge. Sanity-check with Little’s Law, concurrent users = arrival rate x average session duration.

Sources

Published with PostNext

Bogdan

Founder at LoadFocus

Bogdan builds and runs the tools this blog is about. He writes from what the products actually do in production, including the parts that break.

Weekly tips right in your inbox.

Don’t miss out on the latest testing tips and tricks, tools and news related to software testing.

Already +20,000 members in the list.

The Load Testing Platform with AI That Explains Your Results

Load test websites and APIs from 25 cloud regions, monitor page speed and uptime, and get AI analysis that explains your results in plain English.

Cloud load testing, website speed and API monitoring in one place.

Hi there! Can I share a cool thing I'm working on with you?

A software testing newsletter that sparks joy.

Our goal is to create helpful content for software developers and testers.

If you're not into it, you can unsubscribe instantly.