VoidhashDocs
DevelopersHelp centerGuidesAPI reference
Dashboard
Get started
Introduction
Quickstart
How Voidhash works
Glossary
Versions and releases
Concepts
Teams, projects and environments
API keys
Products and perks
Offers
Subscription lifecycle
People and identity
Events
Features
Paywalls in your app
Feature flags and A/B tests
Paywall components
Push notifications
Ad attribution in your app
Client SDKs
React Native
Swift
Kotlin
Web
Your backend
Node.js
PHP
Go
Rust
Other languages
Payment sources
Forward store notifications
Developer tools
CLI
MCP server
Query analytics with VoidQL
Web SDK
Web quickstart
Script tag
Identify users
Evaluate feature flags
Track
Track events
Page views and attribution
Privacy
Privacy and consent
Browser storage
Connect a consent tool
Keep data out of analytics
Revenue and attribution
Sell on the web
Stripe revenue
Web-to-app handoff
Delivery
First-party proxy
Tracking domain
Reference
Web SDK reference
Errors
Troubleshooting

Browser storage

Every localStorage and sessionStorage key and cookie the web SDK sets, and when.

What the web SDK keeps in the visitor's browser, for your privacy policy and cookie banner. With consent pending and storeBeforeConsent: false, it stores nothing; see Ask for consent first.

Local storage

The SDK keeps its state in localStorage under keys that start with vh:2: and a hash of your publishable key:

KeyContentsStored
identity:distinct-idThe anonymous ID, or your user ID after identify().On start and on identity changes.
identity:outboxUndelivered identify() links, with the email, name and traits passed.Until Voidhash confirms the link.
identity:shared-id-used, identity:handed-out-id, identity:given-up-idsState of the cross-subdomain cookie.Only with crossSubdomainIdentity.
consent:analyticsThe visitor's consent choice.On setAnalyticsConsent().
consent:ad-data-sharing-revocationsIDs whose ad data sharing is still to be revoked.From setAdDataSharing(false) until Voidhash writes it, or for 48 hours.
feature-flags:<id>:<keys>The last evaluated feature flags.Unless featureFlags.persist is false.
analytics:queue:<tab>, analytics:owner:<tab>Events waiting to be sent, per tab.While consent is granted.
analytics:sessionSession ID and the time of its last event.While consent is granted.
analytics:fb-traitsThe $fbc and $fbp values last sent.While consent is granted.
analytics:latest-touchThe last campaign, for playStoreUrl().While consent is granted; removed by reset().
network:circuit:<api|ingest>:<origin>Until when requests pause after repeated failures.During an outage.
__keys__An index of the SDK's keys.With the keys above.

The SDK also uses sessionStorage key @voidhash/web:<publishable key>:touch for a campaign seen while consent is pending, a short-lived vh:lock: key in browsers without Web Locks, and a __voidhash_probe__ key it removes at once. Withdrawing consent removes every analytics: key.

The cross-subdomain cookie

With crossSubdomainIdentity on, the SDK sets one cookie, which you should list in your cookie banner. Otherwise it sets none.

NameContentsAttributes
vh_id_<hash of your key>An anonymous ID and short digests of up to three IDs linked to a user. Never a user ID or email.Your registrable domain, Path=/, SameSite=Lax, Secure on https, up to 400 days.
Turn it on only if you trust every subdomain

Every subdomain of your registrable domain can read and set this cookie, including vendor-run ones such as a help center, and any a stale DNS record lets someone take over. The anonymous ID in it gives access to that person's subscriptions and perks through the client API, and setting it decides which person a visitor becomes.

  • It is read and written only while analytics consent is granted. Withdrawing consent deletes it, but another subdomain with a stored grant can set it again, so pass the same answer to every subdomain.
  • A second cookie, vh_id_<hash>_probe, is set and removed at once to find your registrable domain.
  • Safari caps cookies set from JavaScript at 7 days, or 24 hours on pages reached through a link with a click ID from a domain it classifies as a tracker. Signed-in users are unaffected.
  • If the cookie expires before a visitor returns to a subdomain after signing in on another, that subdomain keeps an ID now linked to the user, readable by the next person on a shared browser. Calling reset() on that subdomain replaces the ID.
  • If you turn the option off, the SDK stops reading the cookie; it stays until it expires.

Next steps

  • Privacy and consent: what the SDK collects, and how to ask for consent.
  • Connect a consent tool
  • Page views and attribution

Something wrong or missing on this page? Tell us by email or on Discord.

PreviousPrivacy and consentNextConnect a consent tool

On this page

Local storageThe cross-subdomain cookieNext steps