Background sync: retrying failed requests
Published Updated
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.
What it does
Section titled “What it does”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 registration and event API
Section titled “The registration and event API”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()); }});Availability across browsers
Section titled “Availability across browsers”Background sync is a Chromium (Blink) feature — Chrome, Edge, and Samsung Internet implement it, while WebKit (Safari) and Gecko (Firefox) do not.
- Legend
- Yes
- Partial
- Flag
- No
- Unknown
| Browser / Platform | Support | Versions | Confidence | Source | Notes |
|---|---|---|---|---|---|
| Chrome (Desktop) | Yes | 49 | high | source | — |
| Chrome (Android) | Yes | 49 | high | source | 1 |
| Edge (Desktop) | Yes | 79 | high | source | 2 |
| Firefox (Desktop) | No | — | high | source | 3 |
| Firefox (Android) | No | — | high | source | 45 |
| Safari (macOS) | No | — | high | source | 6 |
| Safari (iOS) | No | — | high | source | 78 |
| Samsung Internet | Yes | 5.0 | high | source | 9 |
| WebView (Android) | No | — | high | source | 10 |
- Derived by browser-compat-data mirroring from Chrome.
- Derived by browser-compat-data mirroring from Chrome.
- No Firefox support is recorded in browser-compat-data.
- No Firefox for Android support is recorded in browser-compat-data.
- Derived by browser-compat-data mirroring from Firefox.
- Implementation tracking: https://webkit.org/b/182565.
- Implementation tracking: https://webkit.org/b/182565.
- Derived by browser-compat-data mirroring from Safari.
- Derived by browser-compat-data mirroring from Chrome Android.
- Implementation tracking: https://crbug.com/40449796.
See the compatibility page for more detail.
What goes wrong
Section titled “What goes wrong”- 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— callingevent.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.
How to detect it at runtime
Section titled “How to detect it at runtime”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);}Where to go next
Section titled “Where to go next”- Background sync: browser support — browser support matrix
- Periodic background sync — a related, separate API