Home / Blog / Consent in the service worker: the requests your page-level wrapper never sees

Consent in the service worker: the requests your page-level wrapper never sees

Published 2026-10-04

Your consent wrapper is solid. It blocks trackers at the page level, waits for the visitor's choice, and your automated tests pass. Then the service worker wakes up and fetches half the internet on its own schedule. Page-level consent wrappers do not see service worker requests, and that blind spot is where trackers are quietly moving.

Why the wrapper misses service worker traffic

Consent wrappers work by controlling what the page is allowed to load: scripts, iframes, images, beacons. A service worker runs on a separate thread with its own fetch handler. Once it is installed, it can make network requests that never pass through the page's resource loading at all. Prefetching, background sync, push notification handlers, and cache warming all originate from the worker, not the document. The wrapper sees none of it, because there is no page context to instrument.

This matters more than it sounds. Service workers commonly prefetch analytics endpoints, sync queued events, and warm caches for routes the visitor has not visited yet. Each of those requests can carry identifiers: client IDs in the URL, device data in headers, or tokens from IndexedDB that the worker reads directly. From a consent perspective, these are tracking requests that fire without any connection to the consent state.

What actually leaks

Three patterns show up repeatedly. First, background sync replaying analytics events that were queued before consent was given or after it was withdrawn. The queue was built with good intentions, offline support, but the replay does not re-check consent at send time. Second, push notification handlers that ping analytics when a notification is received or clicked, including the notification payload and device identifiers, with no consent gate. Third, cache warming and prerender-style prefetching that loads third-party resources for pages the visitor never opened, firing their trackers speculatively.

The common thread is timing. Service worker logic was written to be resilient: retry, queue, sync later. Consent logic needs the opposite: check now, before every send. Retried requests are the exact case where the two conflict.

How to audit your service worker

Start by reading the fetch handler. List every URL pattern it touches and ask for each one whether it needs consent and which category. Then test with the network tab filtered to the worker: in developer tools, enable the service worker request view, decline all consent in a fresh profile, and trigger background sync, push events, and offline replays. Anything that fires to an analytics or advertising domain is a finding.

Automate it. A CI check that installs the worker, sets consent to rejected, triggers sync and push events, and asserts zero tracking requests catches regressions that manual testing misses. Service worker code changes less often than page code, which makes it easy to forget and hard to notice when it drifts.

The fix: gate at the worker, not just the page

The wrapper needs a counterpart inside the worker. Pass the consent state into the worker through the Cache API or IndexedDB, and have the fetch and sync handlers check it before every request that could carry identifiers. Queued events should be stamped with the consent state at queue time and re-validated at replay time; a queue is not consent, and a retry is a new request. Push handlers should skip analytics pings when the relevant category is not granted.

Design the worker to degrade, not to fail open. If the consent state is unavailable, for example during first install before the page has run, the worker should hold non-essential requests rather than fire them. Fail-closed is the correct default for a component the page-level wrapper cannot see.

The bottom line

Consent enforcement that stops at the page boundary is enforcement with a hole in it. Service workers are a separate origin of network requests, so they need their own consent logic: state visible to the worker, checks before every send, and a default that holds rather than fires. Audit the worker the way you audit the page, and the wrapper finally covers everything the site actually sends.

Get a free consent audit of your website

Free consent audit