---
title: "JA4 Fingerprint Decoder"
description: "Decode any JA4 TLS fingerprint field by field — transport, TLS version, SNI, cipher and extension counts, and ALPN — plus a browser-family guess."
kind: snippet
maturity: budding
confidence: high
origin: ai-drafted
author: "Agent"
directedBy: "krow"
tags: [security, networking, fingerprinting, ja4, tls, tools]
published: 2026-05-29
modified: 2026-05-29
wordCount: 449
readingTime: 3
related: [ja4-fingerprint-t13d1516h2, ja4-vs-ja3, bot-detection-2026, tls-fingerprinting-curl-cffi]
url: https://krowdev.com/snippet/ja4-decoder/
---
## Agent Context

- Canonical: https://krowdev.com/snippet/ja4-decoder/
- Markdown: https://krowdev.com/snippet/ja4-decoder.md
- Full corpus: https://krowdev.com/llms-full.txt
- Kind: snippet
- Maturity: budding
- Confidence: high
- Origin: ai-drafted
- Author: Agent
- Directed by: krow
- Published: 2026-05-29
- Modified: 2026-05-29
- Words: 449 (3 min read)
- Tags: security, networking, fingerprinting, ja4, tls, tools
- Related: ja4-fingerprint-t13d1516h2, ja4-vs-ja3, bot-detection-2026, tls-fingerprinting-curl-cffi
- Content map:
  - h2: What the decoder can and can't recover
  - h2: Why the counts matter
  - h2: Reference set
  - h2: Sources
- Crawl policy: same canonical content is exposed through HTML, Markdown, and llms-full; no crawler-specific content gate.

import JA4Decoder from '../../src/components/JA4Decoder.svelte';

A JA4 fingerprint summarises a client's TLS ClientHello into a string like `t13d1516h2_8daaf6152771_d8a2da3f94cd`. The first part is human-readable and fully reversible; the two hashes that follow are not. Paste any JA4 below to break it down.

<JA4Decoder client:visible />

## What the decoder can and can't recover

A full JA4 has three parts, `a_b_c`, and they are not equal:

- **`a` — the readable prefix** (`t13d1516h2`). Six fixed fields: transport, TLS version, SNI presence, cipher count, extension count, and first ALPN. Every field is a direct encoding, so the decoder reconstructs all of it exactly.
- **`b` — the cipher hash** (`8daaf6152771`). A truncated SHA-256 of the client's cipher-suite list, sorted before hashing so extension-order randomization can't move it. SHA-256 is one-way: you can confirm a known list produces this hash, but you can't run it backwards.
- **`c` — the extension hash** (`d8a2da3f94cd`). Truncated SHA-256 of the sorted extensions (with SNI and ALPN removed) plus the signature algorithms in their original order. Also one-way.

So the decoder reads `a` and *matches* `b`/`c` against a small set of known fingerprints. That's the same shape of problem a CDN solves at scale — it can't reverse your hashes either, so it compares them against a fingerprint database. [How Websites Detect Bots in 2026](/article/bot-detection-2026/) covers how that comparison drives a block-or-allow decision.

## Why the counts matter

The cipher and extension counts are the fields people most often misread. Both **exclude GREASE** values (the deliberately random entries browsers inject to keep middleboxes honest). The extension count **includes** SNI and ALPN — they're only removed from the `c` *hash*, not from the count. That's why a modern Chrome reports `15` ciphers and `16` extensions rather than the larger raw numbers you'd get by counting GREASE.

For the meaning of one specific, very common prefix, see the [`t13d1516h2` reference](/snippet/ja4-fingerprint-t13d1516h2/). For why JA3 gave way to this format, see [JA4 vs JA3](/article/ja4-vs-ja3/); for how detection vendors use the fingerprints, see [How Websites Detect Bots in 2026](/article/bot-detection-2026/). To actually *produce* a chosen JA4 from Python, see [TLS Fingerprinting with curl_cffi](/note/tls-fingerprinting-curl-cffi/).

## Reference set

The browser-family guess uses a deliberately small, conservative set drawn from FoxIO's published JA4 database: `t13d1516h2` and the PSK session-resumption variant `t13d1517h2` (both Chromium), `t13d1715h2` (Firefox), and `t13d2014h2` (Safari), plus two full Chrome hashes for desktop version ranges. JA4 has no canonical public reverse-lookup database, so a "no match" doesn't mean the fingerprint is invalid — only that it isn't in this set. A prefix no mainstream browser uses is usually a scripted client.

## Sources

- [FoxIO-LLC/ja4 — JA4 technical spec](https://github.com/FoxIO-LLC/ja4/blob/main/technical_details/JA4.md) — the authoritative field-by-field construction rules.
- [ja4db.com](https://ja4db.com/) — community fingerprint lookups (availability varies by region).