Our marketing site writes nothing to seven storage surfaces, and reads one key that is never there

2026-11-04来源 dev.to WebDevEnglish

A while ago I published a post arguing that PubTrivia needs no cookie banner. The argument was partly wrong, and I corrected it later: the root layout does load one cross-origin script, so the claim that nothing on the page talks to another domain did not survive contact with performance.getEntriesByType("resource").

What I never did was the measurement that the argument actually rested on. So here it is: every place a browser can keep state, checked on the live site.

The seven surfaces

Cookies, on the wire. curl -sI over all 72 URLs in the sitemap: zero Set-Cookie headers. Then the same question for subresources, which curl cannot see, by driving Chrome through twelve pages with the Network domain enabled and reading responseReceivedExtraInfo for every response: 557 requests, zero Set-Cookie headers, including the response from the one off-origin script.

Cookies, in the document. document.cookie is the empty string on all 72 pages. Network.getCookies for https://pub-trivia.app returns an empty array.

localStorage and sessionStorage. localStorage.length and sessionStorage.length are both 0 on all 72 pages, after a full load and a four second wait.

IndexedDB. indexedDB.databases() returns an empty array.

Cache Storage. caches.keys() returns an empty array.

Service workers. navigator.serviceWorker.getRegistrations() returns zero. There is no service worker file in the repository and no offline mode, which also means our web manifest describes an installable app that cannot run without a network.

That holds while using the site, not just while loading it. I typed a web address and a label into the QR code generator, which is a real tool that does real work, and checked again: cookie empty, both storages zero, no databases. The tools compute in the page and keep nothing, which is what their own copy promises.

One read, no writes

There is exactly one storage call a visitor triggers, and it is a read. I trapped Storage.prototype.getItem through Page.addScriptToEvaluateOnNewDocument, so the trap is installed before any page script runs, and captured the stacks:

Storage.getItem("theme") <- https://pub-trivia.app/faq:1:6155 (inline, blocking) Storage.getItem("theme") <- /_next/static/chunks/0efhw0ogo_md6.js (hydration) 

Twice per page, same key, on every page I traced. It comes from next-themes, configured in the root layout as:

<ThemeProvider attribute="class" defaultTheme="dark" enableSystem> 

The library's blocking script reads the stored preference before first paint so the page does not flash, then the hydrating bundle reads it again. Nothing on the public site ever writes it, because the only control that calls setTheme is the toggle inside the logged-in dashboard. So every visitor to the marketing site runs a lookup for a key that, for them, cannot exist, and both reads return null and fall through to the default.

That is not a bug worth fixing, it is one synchronous lookup. It is a nice illustration of something else though: localStorage.length === 0 does not mean nothing touched storage, and if you are auditing a site for privacy you want the trap, not the count.

The off-origin script, measured rather than asserted

The one request that leaves our domain on every page is js.stripe.com/v3/pricing-table.js, loaded from the root layout with strategy="lazyOnload". Measured today: one request per page, no Set-Cookie on the response, no frames created, and customElements.get("stripe-pricing-table") defined on pages that contain no such element.

A caveat for anyone repeating this on their own site: my browser profile already holds cookies for stripe.com, because I use the Stripe dashboard every week. They show up in Network.getCookies for that domain and they have nothing to do with this visit. Third-party cookie state in a working profile is sediment. The question to ask is not "are there cookies for that domain" but "did this page cause a Set-Cookie", and the answer here was no, 557 times.

Our cookie policy covers Stripe's strictly necessary cookies, which is correct for the checkout flow where Stripe's own page does the work. On the marketing pages, the script sits there defining an element nobody uses, which is its own kind of waste and the subject of an earlier correction.

What we give up

Storing nothing is a choice with a bill attached, and it is worth being clear about what we are paying:

  • no memory of a dismissed notice, so anything we ever want to show once has nowhere to record that it was shown
  • no client-side experiment framework, because there is nothing to hold a bucket assignment
  • no attribution beyond what is in the URL, and as I measured yesterday, a query string does not even change our edge cache key, so campaign tags cost nothing and tell us nothing without a server log
  • no theme memory for anybody who is not logged in, which is invisible today only because the public site is dark for everyone

What we get is that there is no consent decision to make, no banner to design, no vendor list to maintain, and no gap between what the cookie policy says and what the page does. That page, incidentally, sets nothing either: it is a document about cookies that is itself cookie-free.

Check it yourself

Open any page on pub-trivia.app, go to the Application tab, and look at Cookies, Local Storage, Session Storage, IndexedDB, Cache Storage and Service Workers in turn. All six are empty. Or paste this into the console:

({ cookie: document.cookie, ls: localStorage.length, ss: sessionStorage.length, idb: (await indexedDB.databases()).length, caches: (await caches.keys()).length, sw: (await navigator.serviceWorker.getRegistrations()).length }) 

Six zeros and an empty string. If you want to see the one read, define a getter on Storage.prototype.getItem first, then reload.

原文:https://www.a8q9.cn/en/article/our-marketing-site-writes-nothing-to-seven-storage-surfaces-and-reads-one-key-th-50edb9.html

相关文章