Consent is checked in a console

A vendor's consent claim is a statement about execution order, and the network panel settles it. The checks to run, and the one request no flag stops.

A dark glass console with four switches, one glowing orange and three cool silver

A vendor telling you its script respects consent is making a claim about execution order, and execution order is one of the few vendor claims you can settle yourself. Open the network panel, deny consent, load the page, and count what leaves the browser before anybody clicks Accept.

A clause promising that a script “does not activate until consent” is unusual among vendor claims, because it predicts events you can watch. There are four of them, on any script, in a browser you already have. Requests leaving for the vendor’s origin. Entries appearing under the storage panel. Listeners registered on window and document. Nodes added to the DOM.

The paperwork answers a different question. It records what the vendor believes it is entitled to store and on what basis, which is a legal argument, and that argument is unchanged by whichever build of the file your CDN served this morning. Order is decided by the code that ran. A vendor ships that code on their own schedule, and nobody re-reads a signed document when they do.

Which is the case for making the check cheap rather than exhaustive. A verification that takes an afternoon happens once, at procurement, against a release that will be replaced. A verification that takes the length of a page load can be run again after any release, by whoever happened to be looking at a slow page.

The pass any vendor can be put through

Load the page twice, once with consent denied and once granted, and compare. The comparison is the output, because an empty network panel is also what a broken tag looks like, and a check that cannot tell those two apart has told you nothing.

Set the panel to preserve its log and to ignore the cache before the first load. Then work through the rows below, all of which apply to any vendor on the page.

What you checkWhere it showsWhat a consent-gated script predicts
Requests to the vendor’s originNetwork panel, filtered by the vendor’s domainAt most the script file itself, and nothing following it
Cookies and web storageThe Application or Storage panel: cookies, localStorage, sessionStorage, IndexedDB, Cache StorageNo entry under any of them, including the databases a cookie banner never lists
Listeners on the pageThe Chromium console, via getEventListeners(window) and getEventListeners(document); Firefox and Safari badge listeners per element in the inspector, which does not cover window or documentNothing registered by the vendor’s file
Nodes in the DOMElements panel, or a query for the vendor’s own root attributeNo injected root, no iframe, no launcher
All four again, with consent grantedThe second loadEvery one of them changes

Two habits decide whether the pass is worth running. Wait longer than feels necessary before reading the network panel, because a script that defers its first beacon to an idle callback or to the first scroll survives a look lasting two seconds and fails a real one. And run the denied case in a profile with no history for the site, since a script reading an identifier it wrote last week has no reason to write one today.

The limit no flag removes: the file still arrives

This is where an inert-stub design stops answering the question, and it is better stated in the vendor’s own words than discovered by an auditor.

An attribute on a <script> tag is read by the script. Reading it requires the file to have been requested, downloaded, parsed and executed, so the vendor’s server has already answered a request carrying the visitor’s IP address, their user agent and the referring URL, and third-party code has already run on the page. What our own loader does after that point is bounded and short: it reads its own tag’s attributes, defines two properties on window, one of them the public PulseBack interface and the other its internal bookkeeping, reads the document’s visibility state and a clock origin for its own document, and stops.

For most teams that boundary is the one they wanted, since consent governs collection and a downloaded file that collects nothing sits on the correct side of it. For a policy forbidding any request to a third-party origin before the click, the boundary sits elsewhere, and no flag inside the file can move it. The only arrangement that satisfies that policy is a tag which is not in the document until the click, meaning the consent platform injects the <script> element instead of configuring it. That is a decision about your page rather than about the vendor’s code, and it is the one an inert stub cannot make on your behalf.

Our own answer, and the zero a test holds

Our tag takes a boot attribute, data-autostart="false", which is the paused case the pass above is looking for. The feedback widget under it behaves like this, and the list is deliberately finite rather than a promise that nothing happens.

Under data-autostart="false"What the loader does
Lifecycle listenersNone installed, on the window or the document
Configuration requestNever sent; the request path returns before reaching the fetch
Session or ingest trafficNone
Cookies and web storageNone, because the loader calls no storage API at all
open()Returns on its first line
Calls made while pausedResolved with a null answer rather than buffered for a later flush
window.PulseBackPresent, and reports itself dormant when asked

The row worth arguing with is the one about queued calls. A host that calls into a paused widget gets nothing back and nothing kept, which loses the call, and the alternative is a buffer that empties the moment consent arrives. Losing it is the safer half of that trade: a queue drained on resume turns a call made before the click into data captured after it, and the timestamps would not show the difference.

None of that is worth much as prose. What makes it checkable is that a browser test drives a real page through the same pass a reader runs by hand. The test subscribes to every request the page makes, keeps the ones aimed at our configuration and session endpoints, loads the tag with the attribute set, calls open(), waits, and asserts that the list it collected is empty. It asserts in the same breath that the widget’s root element is absent from the DOM. Then it calls resume(), waits for the launcher to become visible, and asserts that a configuration request has finally appeared in the list that was empty a moment earlier. An assertion running in a real browser is the difference between a documented intention and a behaviour that cannot regress quietly.

Where the network panel stops being able to decide

The pass has edges, and knowing them is what keeps it from being used as proof of something it never examined.

What it describes is a single load of a single page, made from wherever you happen to be sitting. A tag configured differently per route, or a vendor whose behaviour varies by the visitor’s region, needs the pass run again per case. It also sees only the browser’s side: what a vendor’s server does with a request it has already received is invisible from the Network tab, and that genuinely is a paperwork question. Ours is set out under security.

Storage is a separate question again, and a harder one, because a script that sets no cookie can still hold a stable identifier in a database no consent banner enumerates. Auditing storage rather than cookies is its own procedure, and whether storing anything at all needs consent is a legal question the storage API does not answer. What the file costs the page once it is awake is a third question, answered by the host isolation contract.

The honest summary of all of it: the pass proves what a script did on the load you watched. It does not prove what the next release will do, and it never could. That is the argument for a check short enough to repeat rather than a document thorough enough to file.

Common questions

How do I check whether a third-party script fires before consent?

Load the page twice in a profile with no history for the site, once with consent denied and once granted, and compare four things across the two loads: requests to the vendor's origin, entries in cookies and web storage, listeners registered on the window and the document, and nodes the vendor added to the DOM.

Does a script that sends no requests before consent still contact the vendor?

Yes, if its tag is in the document. Downloading the file is itself a request to the vendor's origin, carrying the visitor's IP address, user agent and referring URL, and it happens before any code in the file can read a consent flag or decide anything.

Why is counting event listeners part of a consent check?

Listeners are the mechanism by which a dormant script wakes up. A file that registered handlers on the document before the click is watching the visitor already, whether or not it has sent anything yet, and no request appears in the network panel until it decides to send.

Can a consent platform stop a vendor script from being downloaded at all?

Only by keeping the script element out of the document until the visitor accepts. An attribute on the tag is read by code inside the file, so the fetch, the parse and the first execution have all happened by the time any flag is consulted.

What does an inert third-party script do while it waits?

That depends on the vendor, and the honest answer for a well-behaved one is short: define its public interface on the window object, read its own tag's attributes, and stop. Anything beyond that, including a timer or a listener, is work the visitor did not agree to.

Why does a consent check have to be repeated after a vendor release?

Because the contract is not re-read when the vendor ships. Execution order is decided by whichever version of the file the CDN served this morning, and a verification done once at procurement describes a release that has since been replaced.

The Notarus team

The people who build Notarus. We write up the decisions behind the product when the reasoning is worth more than the changelog line.

Keep reading

Reports with the evidence attached.

Start free, one script tag.

<script src="…/widget.js" data-key="pb_pub_…">