Skip to content

Service Worker · Concept

Background sync: retrying failed requests

Published Updated

Limited availabilityNot supported in Firefox (Desktop), Firefox (Android), Safari (macOS), Safari (iOS)WICG draft

In one line: Background sync lets a web app’s service worker defer an action — such as sending a queued message — until the browser reports that the device has connectivity again, instead of failing it outright when the network is unavailable.

Flow diagram of background sync: a request fails offline, the page queues it in IndexedDB and calls sync.register('outbox'); the browser holds the tag until connectivity returns, then fires the sync event in the service worker, which replays the queue inside event.waitUntil(). A resolved promise clears the registration, a rejection with lastChance false is retried later with backoff, and a rejection with lastChance true ends the retries.
Background sync: queue, register, replay on the sync event, and the three ways the promise can settle.

When a fetch fails because the device is offline, a page can register a sync request instead of giving up. The browser holds onto that registration and, once it decides the device has connectivity, fires a sync event in the service worker so the app can retry the deferred work — even if the page that originally registered it has since closed.

The API is reached through ServiceWorkerRegistration.sync, which returns a SyncManager:

async function queueMessage() {
try {
await sendMessage();
} catch {
const registration = await navigator.serviceWorker.ready;
if ('sync' in registration) {
await registration.sync.register('send-queued-message');
}
}
}
  • SyncManager.register(tag) registers a one-off sync request under the given tag, returning a promise that resolves once registration completes.
  • SyncManager.getTags() resolves with the list of tags currently registered.

In the service worker, the app listens for the sync event and passes the retry work’s promise to waitUntil(), which extends the event’s lifetime. The user agent can still impose its own execution and lifetime limits on that extended time, so waitUntil() does not guarantee the work finishes — see “What goes wrong” below:

self.addEventListener('sync', (event) => {
if (event.tag === 'send-queued-message') {
event.waitUntil(sendQueuedMessage());
}
});

Background sync is a Chromium (Blink) feature — Chrome, Edge, and Samsung Internet implement it, while WebKit (Safari) and Gecko (Firefox) do not.

See the compatibility page for more detail.

  • Per the spec, while the registering page or worker is still running, the event fires as soon as connectivity becomes available — not necessarily immediately. If that context is no longer running, the user agent should instead run the event at its “soonest convenience,” not on any guaranteed schedule.
  • The spec allows a user agent to impose its own execution and lifetime limits on a SyncEvent — calling event.waitUntil() does not guarantee the browser will let arbitrarily long work finish before terminating the worker.
  • Background sync is unsupported in WebKit (Safari) and Gecko (Firefox) as of this writing — always design the retry as an enhancement, not the only path to success.
  • Per the spec, a failed sync event may be retried according to user-agent-defined heuristics unless it was the final chance, at which point no further attempts are made — retries are not guaranteed to eventually succeed, and this is not a periodic schedule.

Feature-detect SyncManager before registering a tag, and fall back to retrying immediately (or on the next page load) when it’s unavailable:

function supportsBackgroundSync(registration) {
return 'sync' in registration;
}
async function deferOrRetry(registration, tag, retryNow) {
if (!supportsBackgroundSync(registration)) {
return retryNow(); // no Background Sync: retry immediately instead
}
return registration.sync.register(tag);
}

Specifications

SpecificationStatus
Background SyncWICG draft
  • Legend
  • Yes
  • Partial
  • Flag
  • No
  • Unknown
Browser / PlatformSupportVersionsConfidenceSourceNotes
Chrome (Desktop)Yes49highsource—
Chrome (Android)Yes49highsource1
Edge (Desktop)Yes79highsource2
Firefox (Desktop)No—highsource3
Firefox (Android)No—highsource45
Safari (macOS)No—highsource6
Safari (iOS)No—highsource78
Samsung InternetYes5.0highsource9
WebView (Android)No—highsource10
  1. Derived by browser-compat-data mirroring from Chrome.
  2. Derived by browser-compat-data mirroring from Chrome.
  3. No Firefox support is recorded in browser-compat-data.
  4. No Firefox for Android support is recorded in browser-compat-data.
  5. Derived by browser-compat-data mirroring from Firefox.
  6. Implementation tracking: https://webkit.org/b/182565.
  7. Implementation tracking: https://webkit.org/b/182565.
  8. Derived by browser-compat-data mirroring from Safari.
  9. Derived by browser-compat-data mirroring from Chrome Android.
  10. Implementation tracking: https://crbug.com/40449796.

Source data: /compatibility/background-sync.json · Global usage: 72 % (StatCounter 2026-05)

Source: spec · MDN · Last verified 2026-10-03 · Confidence: high (computed from sources)