About

kittens.sh reports the TLS and HTTP/2 fingerprint of whatever connects to it. Not a description of what your client probably looks like — the actual bytes it sent, decoded field by field, taken from the connection you are reading this over.

Why it terminates TLS itself

Almost everything that could sit in front of a web service destroys the thing this service exists to measure. A CDN, a load balancer or a reverse proxy completes the TLS handshake itself and opens a fresh one to the origin behind it. The origin then measures the proxy — the same fingerprint, for every visitor, forever.

So there is nothing in front of this. The server terminates TLS directly, which means it sees the raw ClientHello before any library has parsed and normalised it away. It speaks HTTP/2 itself rather than delegating to a framework, so it keeps the frame ordering that a normal HTTP library discards on the way to handing you a request object. And it serves this page from the same process as the API, so the page you are reading and the fingerprint it reports came over one connection.

That last detail is the reason the number on the front page is trustworthy. If the site were served from one host and the API from another, you would be shown a fingerprint from a different handshake than the one that loaded the page — close enough to look right, wrong often enough to matter.

What is actually being read

A TLS ClientHello is a list of capabilities. Your client says which protocol versions it speaks, which cipher suites it will accept, which extensions it understands, which elliptic curves and signature algorithms it supports, and which key shares it has already generated on the assumption the server will pick one.

None of that is secret and none of it is unusual. What makes it a fingerprint is that no specification says what order to send it in, or which optional pieces to include. Those decisions were made once, by whoever wrote the TLS library, and they are then made identically by every program that uses it. Chrome’s BoringSSL, Firefox’s NSS, Go’s crypto/tls, Java’s JSSE and OpenSSL all announce themselves clearly to anyone who bothers to look at the shape of the message rather than its contents.

The schemes on this site are three different ways of reducing that shape to a string you can compare: JA4 and JA3 over the TLS handshake, and the Akamai HTTP/2 fingerprint over the frames that follow it. Each has its own page here explaining how it is assembled and where it fails.

Why your fingerprint might change

GREASE. Clients deliberately advertise reserved values that mean nothing — RFC 8701 — to catch middleboxes that choke on anything unfamiliar. They are inserted at random positions and every fingerprinting scheme strips them before hashing. A tool that does not will give you a different answer every time and no explanation.

Deliberate randomisation. Chrome permutes its TLS extension order on every connection, on purpose, so that the ordering cannot ossify into an interface that middleboxes depend on. This is what broke JA3 for browsers and what JA4 sorts around.

Something in the path. Corporate TLS interception, a consumer anti-virus product, a VPN client or a debugging proxy will hand you the appliance’s handshake rather than your own. If your fingerprint looks stable but unfamiliar, this is the first thing to rule out.

You upgraded something. A patch release of a TLS library can move a fingerprint. That is usually fine and occasionally the entire point — pinning it in CI turns a silent change into a failed build.

What it stores

Nothing. The fingerprint is computed from the live connection and written back out on that same connection. There is no database, no logging of handshakes, no account, and no key. Responses carry no-store, because a cached fingerprint would by definition belong to somebody else.

The API is open to everything, rate limited only enough to stop it being used as a load generator.

curl -s https://kittens.sh/api/clean

Licensing and credit

JA3 is Salesforce’s, from 2017, and JA4 is FoxIO’s — both BSD-3-Clause, and both implemented here. The wider JA4+ suite (JA4S, JA4H, JA4X and the rest) is under the FoxIO License 1.1, which does not permit monetisation without an OEM licence, so none of it is implemented here. The HTTP/2 fingerprint follows Akamai’s 2017 description of passive HTTP/2 client fingerprinting.

The server is Rust, the site is a static export, and both are on GitHub. Built by Art Freebrey, sponsored by Revnu.

Start here

Inspect your own connection, read the API reference, or start with how JA4 is built.