which chat widgets set cookies before anyone clicks

A visitor lands on your page, reads for ten seconds and leaves without touching the chat. We measured what seven chat widgets leave behind in that case: cookies, localStorage, sessionStorage, IndexedDB, and the servers they talk to. Spookat leaves nothing and talks to one server, our CDN. Every rival that booted wrote something to the visitor’s browser, and some of it is kept for more than a year.
the method, in one paragraph
Same lab as the rest of this series (how we test). Chrome 153, a brand-new profile for every run, 7 runs per widget, measured on 2026-09-27. The page loads, and for 25 seconds nothing is clicked, scrolled or typed. Then we read every cookie in the browser and every localStorage, sessionStorage and IndexedDB entry in every frame. We record names, domains, lifetimes, flags and sizes, never values.
Four widgets refuse to run on a domain their owner didn’t allow (Crisp, Intercom, LiveChat and Chatwoot), and the ids we could use belong to the vendors’ own sites. On our test page, before refusing, none of the four wrote anything. So for those four we loaded the vendor’s own site, where the widget runs, and loaded it again with the widget blocked. A cookie or key counts as the widget’s only if it appears in at least half the runs with the widget and in none without it. That separates the widget from the vendor’s own analytics and ad cookies, which their marketing sites set plenty of.
We didn’t touch any consent banner on those sites: not accept, not reject, not close. Some widgets may behave differently once a visitor has made a choice, and that’s outside this test.
the results
| widget | cookies before a click | longest kept | local and session storage | servers contacted |
|---|---|---|---|---|
| Spookat | 0 | none | none | 1 (cdn.spookat.com) |
| Tawk.to | 3 on the site’s domain | 180 days | 2 localStorage keys, 1 sessionStorage key | 9 hosts on tawk.to |
| Tidio | 0 | none | 1 localStorage key (3 kb) | 2 hosts on tidio.co |
| Crisp | 1 on the site’s domain | 182 days | 4 localStorage keys | 3 hosts on crisp.chat |
| Intercom | 3 on the site’s domain | 270 days | 1 localStorage key, 1 sessionStorage key | 4 hosts on intercom.io and intercomcdn.com |
| LiveChat | 2 on accounts.livechatinc.com (+1 that expires at once) |
400 days | 4 localStorage keys, 1 sessionStorage key, in its iframe | 4 hosts on livechatinc.com |
| Chatwoot | 1 on the site’s domain, 1 on app.chatwoot.com |
365 days | 6 localStorage keys (460 kb) in its iframe | 3 hosts on chatwoot.com, CloudFront and S3 |
Nobody used IndexedDB. Every result was the same in all 7 runs of each widget.
“On the site’s domain” means the widget’s own script wrote the cookie with JavaScript, on whatever domain the page is on. On your site, those would be your cookies, set by someone else’s code.
widget by widget
spookat: nothing
The launcher is one script from cdn.spookat.com. It draws a button and stops. It has no code that reads or writes cookies, and no storage call runs before the click.
After a click, still nothing is stored. The chat panel loads, connects to api.spookat.com, and shows the greeting. Only when the visitor sends their first message does the panel write one localStorage key, so the conversation survives a page reload. That’s the first time Spookat puts anything in the browser: after a person asked for a chat and wrote in it. The widget never sets a cookie, then or later.
tawk.to: three cookies and a token
Tawk.to boots anywhere, so these are its full numbers on a normal page. Before any click it sets three cookies on the page’s own domain:
twk_uuid_<property>, kept 180 daysTawkConnectionTime, a session cookietwk_idm_key, a session cookie
It writes two localStorage keys, one of them a 464-byte token, and one sessionStorage key with the previous page. It opens a websocket to one of several numbered tawk.to servers. Across our 7 runs it used 7 different ones, which is why the table shows 9 hosts.
tidio: one key, no cookies
Tidio set no cookies. It wrote one localStorage key with its state (about 3 kb) and opened a websocket to socket.tidio.co. Its own widget is set to stay hidden on tidio.com, so our page called Tidio’s documented tidioChatApi.show() before any click to show the launcher. That call is the only change to a stock install.
crisp: a session for six months
On crisp.chat, before any click, the widget sets one cookie on the site’s domain, crisp-client/session/<website id>, kept 182 days. It also writes four localStorage keys: the same session, an expiry, and a visit count and last-visit time for its triggers. Its websocket goes to client.relay.crisp.chat.
intercom: an id kept for nine months
On intercom.com the widget sets three cookies on the site’s domain:
intercom-id-<app>, kept 270 daysintercom-device-id-<app>, kept 270 daysintercom-session-<app>, kept 7 days
Plus its state in localStorage (2.4 kb) and a list of played notifications in sessionStorage, which fits the message intercom.com pushes to new visitors. A quieter install may not write that one.
livechat: third-party cookies, partitioned
LiveChat’s cookies sit on the vendor’s domain, not the site’s, and its server sets them: __lc_cid and __lc_cst on accounts.livechatinc.com, kept 400 days, HttpOnly, Secure, SameSite=None, and partitioned. Partitioned means Chrome keeps a separate copy per site the visitor is on, so the same cookie can’t follow them from one site to another. A third, __oauth_redirect_detector, expires as soon as it’s set.
Its iframe from secure.livechatinc.com also writes four localStorage keys, among them an auth token and a “has visited” flag, and one sessionStorage key.
chatwoot: 460 kb in localStorage
On chatwoot.com’s help center (its homepage didn’t load the widget in our tries), the widget set a cw_conversation cookie on the site’s domain, kept 365 days, and a session cookie on app.chatwoot.com. Its iframe wrote six localStorage keys. One of them holds the six most-read help articles, and it’s 460 kb, which is a lot of someone else’s content cached on your visitor’s device just in case.
whose cookie is it
Two different things hide behind “the widget sets a cookie”.
Cookies on your domain. Tawk.to, Crisp, Intercom and Chatwoot write theirs with JavaScript, on the domain of the page they’re running on. To the browser these are first-party cookies: yours. They survive third-party cookie blocking, because they aren’t third-party, and they’re sent with every request to your own server too. A visitor’s cookie list on your site will show twk_uuid_… or intercom-id-… next to your own session cookie.
Cookies on the vendor’s domain. LiveChat’s live on accounts.livechatinc.com, and Chatwoot’s session cookie on app.chatwoot.com. These are third-party on your site. Chrome still allows third-party cookies by default, but LiveChat’s are partitioned, so each site gets its own jar and they can’t link a visitor across sites.
Browsers don’t treat all of them the same. Safari’s Intelligent Tracking Prevention caps cookies written by JavaScript at 7 days, so on an iPhone Tawk.to’s 180-day and Intercom’s 270-day cookies last a week at most. Chrome caps every cookie at 400 days, which is exactly what LiveChat asks for.
the four that refused still called home
On our test page, where Crisp, Intercom, LiveChat and Chatwoot weren’t allowed to run, none of them stored anything. They did contact their servers before giving up: Intercom three hosts (its loader, its CDN and its API, which answered 403), LiveChat two, Crisp two, including a websocket it opened before showing “Invalid website”, and Chatwoot one. If you remove a domain from a vendor’s allowlist and leave the snippet on the page, your visitors still send those requests.
the servers your visitors talk to
Every request carries the visitor’s IP address, their browser’s user agent and, in most cases, the address of the page they’re on. That’s how the web works, and it means every server a widget contacts learns that this visitor was on this page at this moment, whether or not they ever chat.
Before any interaction, on a page where it could boot:
- Spookat: 1 host,
cdn.spookat.com. It’s ours, and Cloudflare sits in front of it, as it does for all our hosts; Cloudflare is a listed subprocessor on our privacy page. We never store an IP. - Tawk.to: 9 hosts across our runs, one at a time for the websocket.
- Tidio, Crisp: 2 and 3 hosts.
- Intercom, LiveChat, Chatwoot: 3 to 4 hosts, including websockets that stay open for as long as the tab does.
A websocket opened before a click is a live connection to the vendor for the whole visit. It’s how a widget can show “we’re online” or push a message, and it’s also a steady signal that someone is on the page.
what it means for your cookie banner
We’re not lawyers, and this isn’t legal advice. Here’s the part that’s plain fact.
The EU rule that consent banners come from, Article 5(3) of the ePrivacy Directive, isn’t about cookies specifically. It covers storing information on a visitor’s device, or reading information already stored there. localStorage and sessionStorage are storage on the device just as much as cookies are. The rule’s exception is for storage that’s strictly necessary to provide a service the visitor explicitly asked for.
A visitor who hasn’t touched the chat hasn’t asked for it. So when a widget writes an id to their browser on page load, you have to decide whether that’s covered by the exception, which is a hard argument to make for a visitor who never chats, or whether the widget belongs behind your consent banner. Most of the widgets in this test put you in front of that question. A widget that stores nothing before a click doesn’t.
Putting the widget behind the banner has a cost of its own. Consent tools can hold a script until the visitor accepts, and most will. Then a visitor who rejects, or simply ignores the banner, gets no chat at all, often the same visitor who had a question before buying. You end up choosing between a banner that covers the chat and a chat that only works for people who clicked “accept all”.
The other way out is a widget that doesn’t store anything until the visitor asks for it. Then the storage question doesn’t come up on page load at all, and it comes up later only for the people who opened the chat and wrote in it, who are exactly the people who asked for the service.
What to ask your vendor, and your lawyer:
- What does the widget write before any interaction, and for how long?
- Can it wait until the visitor clicks, and does waiting break anything?
- Which of its servers get a request before a click, and in which country are they?
- Is there a setting that stops storage before interaction, and what does it turn off?
what we’d want from any widget
- Nothing written to the visitor’s browser before they interact with the chat.
- No connection held open before a click. A button doesn’t need a websocket.
- One server contacted, not a handful, and the vendor can tell you where it is.
- After a visitor starts chatting, one key for the conversation, not an id meant to recognise them for the next year.
- A cookie list you can write in one line of your privacy policy.
That’s the bar we built Spookat to, and the table above is what it looks like when a widget meets it.
how to check your own widget
- Open your site in a private window, so there’s nothing stored yet.
- Open DevTools, go to Application, and look at Cookies, Local storage, Session storage and IndexedDB for your domain and for every frame in the list.
- Don’t touch the page for 25 seconds, then reload the Application view. Anything new came from something on the page. Blocking the widget’s script in the Network tab and repeating tells you which of it is the widget’s.
Our raw data is at /research/cookies-2026-09.json: every run, every cookie with its domain, lifetime and flags, every storage key with its size, and for vendor sites the names that disappeared with the widget blocked. The scripts are in the widget lab. The byte side of the same runs is in the weigh-in.
why ours stores nothing
Not because a lawyer told us to. A launcher has one job before the click: be a button. Nothing about a button needs an id in the visitor’s browser, a connection to our server, or a way to recognise them tomorrow. So Spookat doesn’t do any of that until someone writes in the chat, and after that it keeps exactly one key, the conversation, so the chat survives a reload. No cookies, before or after.
Add a chat that leaves no trace until someone uses it: 14 days, no card.