TLS session tickets speed up reconnects, but a ticket the server issues and you replay is also a linkable ID — no cookie, no JavaScript, no storage API.
Most tracking explanations stop at the browser: cookies, localStorage, a fingerprint computed in JavaScript. But HTTPS has a layer underneath all of that, and it keeps its own state. After a TLS handshake, the server can hand your browser a small opaque blob — a session ticket — that the browser stores and replays on the next connection to skip most of the handshake. It exists to make the web faster. It also happens to be an identifier that the server issues, you present back unchanged, and no cookie-clearing button touches.
Key Takeaways
- Session resumption is a legitimate performance feature. In TLS 1.3 the server sends a
NewSessionTicketmessage after the handshake; the client uses it on a later connection to resume with a pre-shared key instead of repeating the full handshake. - The ticket is an ID by construction. The server creates it, the client stores it without being able to read it, and presents it back verbatim. If the server encodes or simply remembers an identifier behind it, it can recognise the returning client — with no JavaScript, cookie or storage API involved.
- Lifetime is the tracking window, and it can chain. RFC 8446 caps a ticket's advertised lifetime at seven days, but a server can issue a fresh ticket on every resumption, extending the link from visit to visit for as long as the client keeps coming back inside the window.
- The ticket is visible on the wire. It travels in the ClientHello, so a passive observer on the path can correlate connections that reuse a ticket. RFC 8446 tells clients not to reuse a ticket for multiple connections for exactly this reason.
- In practice it is not a global supercookie. Modern browsers scope the session cache so that a third party cannot resume across unrelated sites, and drop it at private-browsing boundaries — but the exact behaviour varies by browser and version, so verify rather than assume.
What Resumption Is For
A full TLS handshake costs time: a round trip to agree on parameters, a certificate to send and verify, a signature to compute. For a page that opens a dozen connections, that adds up. Resumption lets a client and server that have already authenticated each other skip the expensive part.
In TLS 1.3, defined in RFC 8446, this works through pre-shared keys (PSKs). After the handshake completes, the server may send one or more NewSessionTicket messages (section 4.6.1). Each carries an opaque ticket, a lifetime, and the material the client needs to derive a PSK for it. On the next connection the client offers that ticket in the pre_shared_key extension of its ClientHello (section 4.2.11), proves it holds the matching secret with a binder, and the server can accept the resumption instead of doing a full certificate handshake.
The same mechanism enables 0-RTT early data (section 2.3): the client can send application data in its very first flight. The RFC is candid that this comes with trade-offs, notably that early data can be replayed, and spells them out in section 8. For this article the point is simpler: resumption is a good feature, and nothing here is a backdoor. The privacy problem comes from who holds the state and how long it survives — not from a flaw in the cryptography.
TLS 1.2 had the same idea in two forms — session IDs and session tickets (RFC 5077) — so none of what follows is new to TLS 1.3. The linkability concern was raised in the academic literature years ago; Sy, Burkert, Federrath and Fischer's ACSAC 2018 paper Tracking Users across the Web via TLS Session Resumption is the usual reference.
How a Ticket Becomes an Identifier
The ticket is opaque to the client. The browser cannot inspect it; it just stores whatever the server gave it. That opacity is what makes it a useful building block for the server — and what makes it a potential tracking vector.
Two designs are common:
- Self-encrypted tickets. The server packs its session state into the ticket, encrypts it with a key only it holds, and keeps nothing itself. Any server holding the key can decrypt it.
- Server-side lookup. The ticket is just a random handle; the server stores the state in a table keyed by that handle.
Either way, the server controls what the ticket contains and can see the exact bytes you replay. Nothing in the protocol stops a server from putting a per-client identifier in it, or from simply logging which ticket value came back. TLS 1.3 does obfuscate the ticket's age (the obfuscated_ticket_age field), but the ticket identity itself is sent as-is. The result is a stable, server-recognisable ID that rides inside the TLS handshake rather than in an HTTP header or script-readable storage. This is the same shape as the ETag cache supercookie, one layer lower — and unlike an ETag, it is not something page JavaScript can read, write or delete.
The Chaining Problem
A seven-day cap sounds like a natural limit on this. RFC 8446 does say a server MUST NOT use a ticket lifetime greater than 604800 seconds — seven days. But the cap applies to one ticket, not to the identity behind it.
On each successful resumption the server is free to send a new NewSessionTicket. The client replaces the old ticket with the new one, and the next visit presents that. Each link in the chain refreshes the window, so a client that reconnects at least once every few days can be followed indefinitely, with every ticket carrying the same underlying identity forward. The advertised lifetime limits a single ticket; it does not limit a server that keeps re-issuing.
That chaining is the part most explanations skip, and it is why "tickets expire after a week" understates the exposure for a site you visit daily. In practice, browsers often keep their own, shorter limits on how long a cached session is reused, and the in-memory cache disappears when the browser fully quits — so the real ceiling is usually set by the client, not by the seven-day number in the RFC.
Why It Is Not a Supercookie in Practice
It would be wrong to conclude that any site you have ever visited can follow you through TLS tickets. The risk is bounded by how browsers scope the session cache:
- Who can resume which session. A session established while you were on one site should not be resumable by the same third-party server embedded on an unrelated site. Partitioning network state (connections, the HTTP cache, TLS sessions) by top-level site is the general approach, and it mirrors what browsers did for the HTTP cache after cache-based tracking became well known.
- Private browsing and clearing. A private window should start with an empty session cache and discard it on close, and a full "clear everything" or browser restart typically resets the in-memory cache.
Treat this as a description of a class of defence, not a promise about a specific release: how each browser partitions or clears its session cache has changed over time, so we deliberately do not cite a version number here. What does remain is same-site tracking: a single site (or a CDN serving it) can still recognise your returning connections, and a network observer can still correlate connections that visibly reuse a ticket.
RFC 8446's own appendix on client tracking prevention (Appendix C.4) states the design intent: clients should not reuse a ticket across connections, because reuse lets passive observers correlate them. A browser that follows that advice limits what an on-path observer learns, but it does nothing about the server that issued the ticket.
A Second Effect: Resumption Changes the Fingerprint
There is a side effect that matters for the rest of this blog's network-layer coverage. A resumed handshake does not look like a full one on the wire. It adds the pre_shared_key extension (which must be the last extension in the ClientHello) carrying the ticket and binder, and may add the early_data extension. The extension list and the total ClientHello length therefore differ from a first connection.
A TLS fingerprint such as JA3 or JA4 is computed from the ClientHello, so the same browser can produce a different value on a resumed connection than on a fresh one. If you are building detection on top of a TLS fingerprint — or trying to understand why one client shows two hashes — resumption is a source of variance you need to account for. For the mechanics, see TLS fingerprinting explained and the JA4+ suite; for the newer transport, HTTP/3 and QUIC fingerprinting has its own version of the same story.
Where This Sits Among Persistent Identifiers
| Layer | Identifier | Who stores it | Cleared by clearing cookies? |
|---|---|---|---|
| Script | Persistent visitor ID | Page JavaScript, scattered across storage | Often partly |
| HTTP cache | ETag supercookie | Browser cache | No |
| TLS | Session ticket | TLS stack's session cache | Not reliably |
The lesson is not that every layer is a catastrophe — each has browser mitigations — but that "I cleared my cookies" describes only the top row of a stack of places where state can sit.
What You Can Actually Do
Very little of this is observable from inside a page, which is part of the point. Practically:
- Do not count on cookie clearing to reset the TLS session cache. Whether "clear cookies" also drops cached TLS sessions depends on the browser; fully quitting and restarting the browser, or a "clear everything" action, is the more dependable way to drop it.
- Private windows are the cleanest boundary, since they are meant to start and end with no carried-over session state.
- Do not expect a tool to show you your tickets. BrowserInsight does not inspect your TLS tickets or report session resumption state. What it does show is what a server observes about your TLS handshake — protocol version, cipher, ClientHello length and an extension hash — in the network card of the fingerprint check, with the same signal feeding the bot detection tool. Keep in mind that those values describe whichever connection carried the request, which may itself have been a resumed one.


