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:
| Key | Contents | Stored |
|---|---|---|
identity:distinct-id | The anonymous ID, or your user ID after identify(). | On start and on identity changes. |
identity:outbox | Undelivered 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-ids | State of the cross-subdomain cookie. | Only with crossSubdomainIdentity. |
consent:analytics | The visitor's consent choice. | On setAnalyticsConsent(). |
consent:ad-data-sharing-revocations | IDs 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:session | Session ID and the time of its last event. | While consent is granted. |
analytics:fb-traits | The $fbc and $fbp values last sent. | While consent is granted. |
analytics:latest-touch | The 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.
| Name | Contents | Attributes |
|---|---|---|
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. |
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.