JA4S Fingerprint Calculator
Paste a TLS ServerHello as hex and compute its JA4S fingerprint — the JA4+ server-response hash. JA4S combines the transport (t for TCP, q for QUIC), the negotiated TLS version, the extension count, the chosen ALPN protocol, the single selected cipher suite, and a truncated SHA256 of the ServerHello extension list (GREASE kept, in wire order). Input may start at the TLS record header (16 03 ...), the handshake header (02 ...), or the ServerHello body; spaces, colons, dashes, dots, commas, and a 0x prefix are ignored. Runs in your browser; nothing is uploaded.
About this tool
JA4S Fingerprint Calculator computes the JA4S fingerprint of a TLS server from its ServerHello, given as a hex string. JA4S is the server half of the JA4+ suite (FoxIO): where JA3/JA4 fingerprint the client's ClientHello, JA4S fingerprints the server's response — the version, cipher and extensions the server chose. Pairing a client fingerprint with the server's JA4S gives a fuller picture of a TLS session for threat-intel, scanning and inventory work.
How JA4S is built
JA4S is an a_b_c string:
(t|q)(version)(extcount)(alpn) _ (cipher) _ (sha256_of_extensions)
- transport —
tfor TCP,qfor QUIC. - version — the negotiated TLS version as a 2-char code (
13,12,11,10,s3,s2), taken from the supported_versions extension if the server sent one, otherwise from the ServerHellolegacy_version. - extcount — the number of extensions in the ServerHello, as 2 digits
(capped at
99). - alpn — the first and last character of the ALPN protocol the server
selected (e.g.
h2→h2),00if none was negotiated,99if the value is non-ASCII. - cipher — the single cipher suite the server chose, as 4 lowercase hex
characters (e.g.
c02b). - extension hash — the first 12 hex characters of the SHA256 of the
comma-joined extension-type list (each type as 4-char hex), in the order they
appear on the wire. If the ServerHello has no extensions this is
000000000000.
GREASE is kept
Unlike JA3, JA4S keeps GREASE values in the extension list and does not
sort it — the extensions are hashed exactly in wire order. (GREASE, RFC 8701,
is the set of reserved 0x0a0a, 0x1a1a, … 0xfafa values endpoints sprinkle
in to keep the ecosystem tolerant of unknowns.)
TCP vs QUIC
The transport character is the only part you have to tell the tool, because it
isn't carried in the ServerHello bytes themselves. Leave the QUIC handshake
box unchecked for an ordinary TLS-over-TCP ServerHello (prefix t); check it
when the handshake was carried over QUIC (prefix q).
What you get
Alongside the JA4S string, the result includes the raw variant JA4S_r
(the same a and b parts, but with the extension list shown un-hashed), the
negotiated TLS version, the chosen cipher, the full extension list, and the
selected ALPN protocol.
Where to get the bytes
A ServerHello is the server's first handshake reply. In Wireshark, expand the
TLSv1.x Record Layer → Handshake Protocol: Server Hello and copy the bytes
(right-click → Copy → …as a Hex Stream), or take them from a tcpdump /
openssl s_client capture. You can paste the whole TLS record (starting
16 03 ...), just the handshake message (starting 02 ...), or the
ServerHello body directly — all three are accepted. Spaces, colons, dashes,
dots, commas, and a leading 0x are ignored.
Common uses
- Compute the JA4S of a captured ServerHello to match it against a threat-intel feed or to fingerprint a server stack.
- Confirm that a server (or a TLS-terminating proxy / load balancer) produces the JA4S you expect.
- Pair a client JA4 with the server's JA4S to characterise a whole session.
Notes
JA4S is a heuristic, not a cryptographic identifier — different servers can produce the same JA4S, and a server's response can vary with the client's offer. Treat a JA4S match as a signal, not proof. Everything runs locally in your browser; the ServerHello bytes are never uploaded.
FAQ
What exactly do I paste — the whole packet or just part of it?
Any of three shapes works: the full TLS record (hex starting 16 03 …), the
handshake message (starting 02 …), or the bare ServerHello body. The parser
figures out which one it has. Spaces, colons, dashes, dots, commas, and a leading
0x are stripped, so a Wireshark "Copy as Hex Stream" or a colon-separated dump
pastes straight in. An odd number of hex digits is rejected.
Why doesn't my JA4S match the JA3S I computed for the same server?
They're different algorithms, not different spellings. JA3S is an MD5 over a
sorted, GREASE-stripped field list; JA4S keeps GREASE extensions, hashes the
extension types in wire order with SHA256 (truncated to 12 hex chars), and
encodes version/ALPN/cipher into a readable a_b_c prefix. The two values are
not comparable — match JA4S against JA4S feeds only.
When should I tick the QUIC handshake box?
When the ServerHello was carried inside a QUIC handshake (e.g. HTTP/3) rather
than TLS over TCP. The transport letter — t or q — is the one component that
isn't present in the ServerHello bytes themselves, so it's the only thing you
have to tell the tool. Getting it wrong shifts the whole fingerprint.
My fingerprint ends in 000000000000 — is that an error?
No — it means the ServerHello contained no extensions, so the extension-hash
part is defined as twelve zeros. Similarly, 00 in the ALPN position means the
server didn't negotiate an ALPN protocol. Both are common with older TLS 1.2
servers.
Developer & Automation Access
Run it from the terminal
Same engine as this page, headless — via the gizza CLI:
gizza tool ja4-server-fingerprint "16 03 03 ... (a TLS record begins with 16 03; a ServerHello handshake with 02)"New to the CLI? Get gizza →
Open it by URL
Pre-fill and auto-run this tool with query parameters — the names match the API/CLI:
https://gizza.ai/tools/ja4-server-fingerprint/?server_hello=16%2003%2003%20...%20%28a%20TLS%20record%20begins%20with%2016%2003%3B%20a%20ServerHello%20handshake%20with%2002%29&quic=trueMachine-readable descriptor: tool.json — title + parameters JSON Schema for agents.
