tls.alpagot.net · port 443 · the hello before the conversation

Your handshake,
fingerprinted.

Before a single byte of HTTP moves, your client introduces itself with a TLS Client Hello — and that introduction is surprisingly distinctive. This page shows you yours.

YOUR CLIENT HELLO, DISSECTED
client address216.73.216.209
TLS protocolTLSv1.3
SNI server nametls.alpagot.net
chosen cipherTLS_AES_128_GCM_SHA256
chosen cipher (IANA id)4865
JA4 fingerprintt13d1011h2_61a7ad8aa9b6_3fcd1a44f3e3
JA3 (MD5)d9863d9e31a3037ffe7167e5c04a528c
handshake bytes sent3424
HTTP versionHTTP/2
transporttcp
bandwidth estimate (bps)-1
ciphers offeredTLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:TLS_EMPTY_RENEGOTIATION_INFO_SCSV
ciphers offered (hex)130213011303C02CC02BCCA9C030C02FCCA800FF
ciphers digestWUS9+h7TjVkF9aEouGuCEMjGQGA=
extensions offeredsession_ticket:signature_algorithms:extended_master_secret:server_name:psk_key_exchange_modes:supported_groups:status_request:application_layer_protocol_negotiation:ec_point_formats:supported_versions:key_share
extensions (hex)0023000D00170000002D000A00050010000B002B0033
extensions digestnJLCrQNOsnP0BUTRyo5RD1VSS7k=

Read by VCL in a fraction of a millisecond at the CMH POP (cache-cmh1290090-CMH) and written straight into this HTML. No JavaScript collected anything.

WHAT THIS IS

The most honest thing your client sends

Everything above comes from the handshake that secured this very connection, via Fastly’s client connection variables. The cipher suites and extensions your client offered, in the exact order it offered them, are decided by its TLS library — not by any setting you can casually change. That makes the offer list a signature.

FINGERPRINTS

JA4 and JA3: a name for your TLS stack

Hash the interesting parts of a Client Hello in a defined order and you get a compact fingerprint that groups clients by their TLS implementation. JA3 (the MD5 above) did this first; JA4 is its structured successor — human-skimmable segments covering protocol, SNI presence, cipher and extension counts and digests. Two very different-looking user agents with the same fingerprint are probably the same software wearing different name tags, which is why these hashes earn their keep in bot defence and abuse triage.

Fastly computes both at the edge as tls.client.ja4 and tls.client.ja3_md5 — JA4 landed in VCL in 2024, so if you have not looked at this variable family in a while, that one is new.

FOR MACHINES

Compare clients from the command line

The plain-text version lives at /raw. Fetch it with two different tools and diff the fingerprints — curl and your browser will not agree, and that disagreement is the entire point.

# same page, different fingerprint $ curl -s https://tls.alpagot.net/raw $ curl -s --tlsv1.2 --tls-max 1.2 https://tls.alpagot.net/raw
NOT JUST FOR SHOW

The same variables drive edge logic

Everything on this page is a live VCL expression, evaluated on every request before your origin is ever involved. That means each value can conditionalise the request flow — route, block, redirect, rewrite, rate limit, or vary the cache on it. A taste, in this site’s dialect:

# vcl_recv - rate limit by TLS stack, not by IP if (table.contains(bad_ja4s, tls.client.ja4)) { error 429; } # vcl_log - fingerprint prevalence across your traffic log "syslog " + req.service_id + " insight :: " + tls.client.ja4;

The other half is insight: every variable here can be emitted through Fastly’s real-time log streaming from vcl_log — per-request, from every POP, with no JavaScript beacons or client-side analytics. Stream them into your warehouse and you know your audience’s client stacks and fingerprint prevalence at whatever depth you like.

UNDER THE HOOD

One service, three hostnames, zero origins

geoip, tls and device are a single origin-less Fastly VCL service, routed by req.http.host. Every response is a synthetic response: the variables above are captured into request headers in vcl_recv (most are not available in vcl_error, where the page is assembled) and interpolated into the HTML at the POP nearest you. Values echoed from client-controlled input are HTML-escaped in VCL first.

The whole family works this way — see http.alpagot.net and h3.alpagot.net for the other experiments, and Fastly’s VCL variables reference for everything the edge can see.

WHO BUILT THIS

Richard Alpagot

Senior Cloud Engineer at Fastly, collector of small sites that explain themselves.