Investigate cookies, device access and consent technically
Connect technical observations to TDDDG and GDPR while identifying the limits of the instruments.

A visible banner does not establish what the browser actually does
A website can display a consent dialog while optional resources have already loaded. The useful evidence concerns actual accesses, stored information and transmitted requests. This article investigates a complete original browser example that separates the decision interface from its technical consequences. It records the situation before any choice, after refusal, after permission and after withdrawal. Each transition has a stated expected effect rather than an attractive but unverified status message.
The example has one deliberately narrow optional measurement purpose. An allowed JavaScript module creates a known cookie and a known localStorage entry. Only an explicit demonstration action sends the fixed text demo-click to an original test collector. No analytics company, advertising network or real visitor profile participates. This small scope lets us attribute every observed event to a specific controlled action. It also makes the difference between observing behavior and approving a real service particularly clear.
Seventy-one executed Edge conditions cover this implementation. They include light and dark presentation, narrow layouts, persisted choices and deliberately delayed requests. They are not a general certification of consent platforms. Instead they demonstrate an investigation method that can be extended to the actual services, purposes and information flows of a particular website. The technical evidence remains useful precisely because its boundary is visible.
Separate access to a device from later processing
Section 25 of the German TDDDG addresses storing information on terminal equipment and accessing information already stored there. Informed consent is the statutory starting point. The two exceptions concern transmission of communications and strict necessity for an explicitly requested digital service. A familiar cookie name or an address on the operator’s own domain does not by itself decide whether an exception applies. The actual operation and purpose require assessment.
The GDPR also requires consideration where personal data is processed. A request without an obvious identifier can still involve personal information. Device protection is likewise not confined to cookies containing personal data. The German supervisory authorities’ orientation paper explains the different areas of application. An inventory therefore connects storage mechanisms, transmission paths and legal analysis without compressing them into a single misleading cookie list.
Our optional measurement is intentionally held behind permission. That decision does not establish that any real measurement service becomes lawful when given the same label. Information about purposes, providers, recipients, retention and subsequent processing has to match the real use. This small source example does not implement that complete service documentation. It answers the narrower technical question of whether its declared permission controls loading and starting in the exercised workflow.
Start with genuinely fresh browser state
The investigation begins in a new browser context without existing decisions or measurement values. Otherwise an earlier stored permission could explain an immediate start while hiding first-visit behavior. The fixture initially captures the context’s complete cookie inventory, localStorage entries and requests received by its own optional service. All three inventories are empty before the first choice. This gives the later transitions an identifiable baseline.
The ordinary demonstration action is also pressed before a choice. It remains usable but produces no measurement request. This is an exercised interaction rather than a screenshot of a covered page. Only afterwards is a decision applied. For every subsequent stage, the record can distinguish a newly created item from something retained from an earlier state. Independent browser contexts prevent the light and dark runs from inheriting each other’s permission.
A real website may additionally use other storage systems, frames, service workers, redirects and dependent resources. Our three selected inventories do not exhaust that larger architecture. They cover the explicitly limited original experiment. An actual audit extends its instruments to the mechanisms really present. An empty cookie panel alone should never be promoted into evidence that the entire website has performed no device access or transmission.
Connect every optional resource to its trigger and effect
Each optional resource needs an identified source, trigger and consequence. Here the source is a module delivered by the second test service. Its trigger is a current or previously stored allowed choice. Its measurement effect begins only when the controller explicitly invokes its exported start function. There are no hidden top-level storage operations in the module. Reading the source and observing the requests establish complementary parts of that statement.
This separation matters when assessing other integrations. Blocking a documented start call is insufficient if merely importing a dependency executes storage or transmission code. In our source the top level only defines the function. The browser test nevertheless records the module request because transferring that file is itself a network event. Absence of a measurement callback does not mean absence of a resource request.
A real inventory also follows dependencies and changing destination addresses. Not every request serves the same purpose, so labels alone should not automatically classify requests as analytics, security or necessary operation. The technical finding first states what happened after which trigger. A justified purpose assessment can then use that reproducible account. This approach is much stronger than assuming that a vendor’s file name reliably describes all behavior inside the file.
Different ports are different origins but not a real third-party site test
Both original HTTP services run on 127.0.0.1 using different ports. They therefore have different origins while sharing a host and site. This is explicitly not a test of real third-party cookies or cross-site browser blocking between unrelated internet services. That limitation is essential: origin and site describe different boundaries. A successful loopback module fetch cannot establish compatibility or lawfulness for an arbitrary external integration.
Imported code runs in the calling document’s context. Its localStorage accesses therefore concern that document origin rather than automatically belonging to the address that supplied the module. The cookie is likewise written through the document. We observe those actual entries in the browser inventory. We do not describe them as if a real remote analytics provider had installed storage in a separate embedded context.
The original collector receives only the fixed demonstration text. Fetch uses credentials omit, so that request does not send browser credentials. This does not make network communication generally free of information. The participating services still receive technical connection information. A real assessment records the actual destination systems, their logging behavior and the full payload. Our isolated same-host arrangement intentionally leaves those production questions open for the real integration.
Make refusal a real state that survives reload
The refusal button stores the explicit choice deny with the example’s version number. It imports no optional module and creates no measurement value. Reload recognizes that choice and leaves ordinary interaction available. Refusal is thus an actual state with observable behavior, rather than merely closing a dialog while measurement continues behind it. The test checks both the initial action and the subsequent reload.
Storing the choice is itself a storage operation and remains in the inventory. Its legal assessment depends on its actual purpose and implementation. This article does not claim that any entry named preference automatically qualifies for an exception. In our source the small versioned value serves only to recover this choice and contains no visitor-profile identifier. That declared function should be kept distinct from the measurement entries created after permission.
The two initial choice buttons use the same color treatment. Their order and visible keyboard focus are exercised. Equal CSS alone does not establish freedom of choice, sufficient information or lawfulness of an entire consent interface. Technical investigation and meaningful decision design complement one another. A passing display condition should not be allowed to mark unrelated substantive requirements as satisfied.
Apply permission only after preparing the chosen state
When permission is selected, the controller first attempts to persist the decision. If that specific preparation fails, the optional module stays unloaded. The controlled failure test replaces the storage-writing function with an exception. No module request and no measurement cookie are subsequently created. The visible message reports that the choice could not be saved and that measurement remains off. This is an executed failure path rather than a proposed fallback.
After successful preparation, the module is fetched and its start function is invoked. Only that function creates demo_metric in localStorage and demo_measurement as a cookie. A further demonstration action then reaches the original collector. Its receipt record contains exactly the intended literal body. The experiment therefore connects permission to observed behavior rather than relying only on a private Boolean inside the controller.
Repeated permission clicks must not create duplicate action listeners. The source retains the module promise and the current stop function. The browser run repeats the choice several times, performs one action and observes exactly one additional collection request. This result describes the tested source. It does not extend automatically to another module whose initialization behavior or dependencies differ. A replacement needs its own corresponding checks.
Withdrawal must reach active execution
The visible stop button immediately changes the current state to off. Its AbortController removes the original action listener, the known measurement storage entry is removed and the known cookie is expired. The choice deny is then saved. Another demonstration action creates no further collector request. Reload also leaves the optional module disabled. These are separate observed outcomes, not deductions from the button’s wording.
The GDPR requires withdrawal to be as easy as giving consent. The European Data Protection Board’s guidelines explain that practical requirement. Our small example supplies a directly reachable single action. It does not implement a full multi-provider purpose manager. A real interface must make the actual permissions and their withdrawal understandable and reachable according to the genuine service design.
Cleanup is deliberately limited to the example’s known measurement values. It neither promises to remove all cookies nor to erase arbitrary remote information retrospectively. The stored decision itself remains visible because it serves a different function. Additional stores and services require their own reliable withdrawal paths and handling of existing information. Calling a function stop does not establish that every participating system has actually stopped.
A late module response must not overtake a newer withdrawal
Asynchronous code can accidentally revive an earlier choice. The fixture therefore deliberately delays the response to the module request. Permission starts that request, then withdrawal occurs before the file arrives. The request itself has already begun and remains part of the record. The meaningful question is whether the continuation starts measurement after the newer decision has superseded its authority.
Every choice increments a revision number. The waiting invocation retains its earlier revision. Once the module resolves, it compares that ticket with the current revision and checks whether permission is still current. Following withdrawal, the comparison fails and the start function is not invoked. The actual browser case subsequently finds no measurement cookie, no measurement value and no active collector callback.
This mechanism protects the original declared start path. It cannot undo unknown top-level side effects inside an arbitrary imported file. A different module that acts immediately during evaluation would have different prerequisites. Source behavior, loading order and dependencies therefore belong to technical service selection. A revision ticket is a useful part of a controlled contract, not a universal off switch for all third-party code.
A request already received cannot be called back
A second timing experiment delays the collector response. Before withdrawal, the request actually reaches the original server and is recorded. Withdrawal aborts the still-open browser operation and prevents subsequent action requests. The event already recorded at the collector remains present. The fixture intentionally preserves that fact instead of clearing its log to create an artificially reassuring result.
An aborted fetch is consequently not proof that the recipient received nothing. A missing visible success result likewise does not prove absent transmission. For an actual investigation, browser observations and appropriate server evidence need to be considered together. Our experiment performs no external erasure and claims no retrieval of information that has already arrived at another system.
After withdrawal there are therefore several distinct questions. Future optional execution must stop. Known storage values need the handling defined for them. Previously processed information needs assessment against the actual data inventory and applicable obligations. These deserve separate findings. A single green disabled status would hide their different meanings and could encourage an operator to rely on a result that was never established.
HttpOnly demonstrates why document.cookie is an incomplete inventory
For a separate controlled probe, the original server deliberately sets an HttpOnly cookie through an HTTP response header. This route is not part of the ordinary first page load and is not described as legally necessary. The full cookie inventory of the browser context subsequently shows both its name and HttpOnly attribute. Reading document.cookie does not reveal that same cookie.
This exercised contrast defeats a common mistaken inference: an empty JavaScript cookie string does not establish an empty cookie store. The measurement cleanup also does not pretend to delete this separate server probe. It remains visible after refusal until the explicit server route expires it with a matching response. Both presentation runs actually exercise that server-controlled removal.
Real investigation therefore combines browser storage tools, response headers and the relevant store views. MDN documents the role of document.cookie and the HttpOnly boundary. Neither a familiar name nor visibility in one tool settles the full legal assessment. This probe gives a technical finding with a known creation mechanism. Its value lies in making the instrument’s blind spot directly observable.
Do not treat malformed or obsolete state as new permission
The test inserts a stored version zero with an allow value and confirms that it starts no optional import. Damaged JSON is also ignored without optional effects. Only the known version and two defined choice values are recognized. Missing or unrecognized state is not silently promoted into permission. Ordinary demonstration interaction remains available throughout these cases.
The version number represents the example’s own data format. It cannot establish that changed purposes or providers remain covered by an older decision. A real consent system needs traceable information about what the recorded choice actually meant. This source invents neither a universal validity duration nor unlimited continuation. Meaningful changes require assessment against the real information and permissions rather than a convenient numeric flag alone.
Further failure states need additional cases. The demonstrated storage exception specifically concerns preparation of permission. Other browser restrictions, multiple open tabs, broken remote modules and a withdrawal that cannot persist are not exhaustively assessed here. The report names this boundary so that a count of passing conditions is not mistaken for evidence about circumstances that the experiment never exercised.

Review 71 executed browser conditions
The saved report contains actual cookie inventories, localStorage entries and the original services’ receipt records. Light and dark presentation each begin with fresh state. Layouts at 320, 390 and 1440 pixels remain horizontally contained. The first keyboard choice reaches refusal with visible focus. These findings belong to the displayed source and its documented conditions, rather than to an unspecified implementation with similar-looking controls.
The illustrations contain unchanged browser pixels after permission and after withdrawal. Their accompanying matrix uses the stored actual outcomes. There is no invented trend line or fabricated success percentage. The visual connects a visible choice to retained values and a reached network effect. A reader can therefore understand the operational difference without having to infer it from every line of JavaScript.
For a real website, an acceptance record specifies browser, review time, starting state, exercised interaction and observed effects. Later changes to integrations or purposes require appropriate fresh checks. A one-time screenshot cannot cover silent changes to a remote dependency indefinitely. Retaining the method and evidence makes later differences explainable without presenting a historical test as permanent assurance.
Read the complete small implementation
The following four files contain the actual HTML interface, presentation, decision controller and optional module. In the isolated fixture, the main server supplies data-optional-origin with the second service’s address. That service delivers the module with the corresponding cross-origin response permission. The experiment handles only the declared demonstration values and fixed collector body. Its source listing does not conceal a real analytics account behind a placeholder.
Adoption requires explicit configuration of addresses, permissions and the real website’s security policies. Replacing the module URL with an unknown measurement provider is insufficient. Full information and management for multiple legal purposes are also outside this source. The useful property is the readable execution boundary: without recognized permission there is no optional import, and without still-current permission there is no explicit start.
For every additional integration the central investigation remains the same. What actually happens before choice, after refusal, following permission and after withdrawal? Evidence from those transitions links the visible interface to real accesses. The applicable legal basis and accurate information about the actual service can then be assessed with that evidence in view. A well-documented boundary supports decisions much better than an unqualified compliant label.
example.html
<main><h1>Gerätezugriffe im Beispiel untersuchen</h1>
<p>Der separate Demo-Dienst wird erst nach Ihrer Auswahl geladen. Es sind keine realen Messanbieter eingebunden.</p>
<section aria-labelledby="choice-title"><h2 id="choice-title">Optionale Demo-Messung</h2>
<p>Das Modul setzt ein Demo-Cookie und einen Eintrag im Browserspeicher. Beim Auslösen der Beispielaktion übermittelt es nur den Text demo-click an den separaten lokalen Demo-Dienst.</p>
<div class="actions"><button id="deny" type="button">Ohne optionale Messung</button><button id="allow" type="button">Optionale Messung erlauben</button></div>
<p id="choice-status" role="status">Noch keine Auswahl. Die optionale Messung bleibt aus.</p>
<button id="withdraw" type="button">Optionale Messung stoppen</button></section>
<section><h2>Eine Aktion ausführen</h2><button id="demo-action" type="button">Beispielaktion ausführen</button><p>Die Schaltfläche bleibt auch ohne Messung bedienbar.</p></section></main>example.css
*{box-sizing:border-box}body{margin:0;background:#f8f2e8;color:#202428;font:18px/1.5 Arial,sans-serif}main{max-width:50rem;margin:auto;padding:2rem}h1{font-size:2rem;line-height:1.2}section{border-top:5px solid #cd0712;padding:1rem 0;margin:1.5rem 0}.actions{display:flex;gap:1rem;flex-wrap:wrap}button{font:inherit;min-height:48px;padding:.75rem 1rem;border:1px solid #c90713;border-radius:4px;background:#c90713;color:#fff;max-width:100%}:focus-visible{outline:3px solid #ce0713;outline-offset:4px}#choice-status{font-weight:bold}@media(max-width:450px){main{padding:1.2rem}h1{font-size:1.65rem}.actions{display:grid}}@media(prefers-color-scheme:dark){body{background:#1d2225;color:#faf1e4}:focus-visible{outline-color:#ff9d92}}gate.mjs
const choiceKey='demo-choice-v1',version=1;
const origin=document.documentElement.dataset.optionalOrigin;
const status=document.querySelector('#choice-status');
let current=null,revision=0,modulePromise=null,stop=null;
function readChoice() {
try {
const saved=JSON.parse(localStorage.getItem(choiceKey));
return saved?.version===version&&['allow','deny'].includes(saved.choice)?saved.choice:null;
}catch{return null;}
}
function storeChoice(choice) {
try{localStorage.setItem(choiceKey,JSON.stringify({version,choice}));return true;}
catch{return false;}
}
async function choose(choice,persist=true) {
const ticket=++revision;
if(choice==='deny') {
current='deny';stop?.();stop=null;
document.cookie='demo_measurement=; Path=/; Max-Age=0; SameSite=Lax';
try{localStorage.removeItem('demo_metric');}catch{}
const stored=!persist||storeChoice('deny');
status.textContent=stored?'Optionale Messung ist aus.':'Messung gestoppt. Die Auswahl konnte nicht gespeichert werden.';
return;
}
if(persist&&!storeChoice('allow')){status.textContent='Die Auswahl konnte nicht gespeichert werden. Messung bleibt aus.';return;}
current='allow';status.textContent='Das erlaubte Demo-Modul wird geladen.';
try {
modulePromise??=import(origin+'/tracker.mjs');
const module=await modulePromise;
// A later withdrawal invalidates this earlier asynchronous continuation.
if(ticket!==revision||current!=='allow')return;
stop??=module.startMeasurement({collector:origin+'/collect'});
status.textContent='Optionale Demo-Messung ist aktiv.';
}catch{status.textContent='Das optionale Demo-Modul konnte nicht geladen werden.';}
}
document.querySelector('#allow').addEventListener('click',()=>choose('allow'));
document.querySelector('#deny').addEventListener('click',()=>choose('deny'));
document.querySelector('#withdraw').addEventListener('click',()=>choose('deny'));
const initial=readChoice();if(initial)choose(initial,false);
tracker.mjs
// No storage access or requests at module top level.
export function startMeasurement({collector}) {
const controller=new AbortController();
localStorage.setItem('demo_metric','1');
document.cookie='demo_measurement=1; Path=/; SameSite=Lax';
document.querySelector('#demo-action').addEventListener('click',()=>{
fetch(collector,{method:'POST',credentials:'omit',headers:{'Content-Type':'text/plain'},
body:'demo-click',signal:controller.signal}).catch(()=>{});
},{signal:controller.signal});
return ()=>controller.abort();
}
States in the original experiment
| State | Known measurement values | New action request |
|---|---|---|
| Before choice | None | No |
| Refusal | None, decision deny remains | No |
| Permission | demo_measurement and demo_metric | Yes, only demo-click |
| Withdrawal | Known measurement values removed, deny remains | No |
| Withdrawal after collector receipt | Previously received event remains recorded | No further requests |
| Separate HttpOnly probe | Only full browser inventory sees server cookie | Independent original probe route |
Technical inventory checklist
- Begin with fresh state and a documented browser.
- Record resources, storage and transmissions before choice.
- Attribute every access to its actual purpose and trigger.
- Assess device access under TDDDG separately from personal-data processing.
- Exercise refusal, permission and withdrawal.
- Repeat after reload and with unrecognized older state.
- Exercise late initialization after withdrawal.
- Distinguish received information from prevented future requests.
- Do not use document.cookie as the complete storage inventory.
- Reassess changes in services, purposes and information.
Primary sources and review scope
Sources reviewed on 9 November 2026. Normative framework: section 25 TDDDG, GDPR and linked supervisory guidance. The technical investigation is an original narrow experiment with 71 executed Edge conditions. Two ports of the same loopback host create different origins but do not test real third-party cookies. This is not a complete consent platform or approval of an external service.








