Context
About six months ago (Jan 2, 2026) ntop published an analysis ("Is JA4 Now Obsolete?", https://www.ntop.org/is-ja4-now-obsolete/) showing that JA4 can produce different fingerprints for the same client across consecutive connections. Their reproduction used Safari as the example: Safari uses the TLS 1.3 pre-shared key extension (RFC 8446) to resume a previous session, and the presence/parameters of that extension (along with related "ephemeral" extensions like session_ticket and padding) change the JA4_b hash even though the underlying client/library is unchanged.
We want to flag that Safari is just the example ntop happened to use — we've independently observed the same instability (fresh handshake vs. resumed handshake producing different JA4 values for the same client) across several different browsers, not just Safari. This isn't a Safari-specific quirk, it's a structural property of how JA4 treats connection-state-dependent extensions.
ntop's own mitigation in nDPI is an opt-in flag (tls,metadata.ja_ignore_ephemeral_tls_extn) that strips a defined set of "ephemeral" extensions (session ticket, pre-shared key, padding) before computing JA4, restoring a stable fingerprint for the same client across reconnects. Their writeup explicitly frames this as a stopgap "until a JA5 standard is published."
Prior art / existing implementation
We ran into this same instability problem and needed a stable JA4, so we already implemented and upstreamed a fix as a PR to the fingerproxy Go library:
wi1dcard/fingerproxy#42
Summary of that implementation, for reference:
- Adds an opt-in
StableJA4Fingerprint alongside the existing raw JA4, leaving raw JA4 behavior unchanged.
- Excludes three extensions from both the extension count and the extension hash input:
padding (0x0015), session_ticket (0x0023), pre_shared_key (0x0029) — the same set ntop identifies as ephemeral.
- Includes test coverage for full vs. resumed/PSK handshakes (Chrome), each individually-ignored extension, and confirmation that non-ephemeral extensions still affect the stable hash as expected.
This isn't meant as "the" reference implementation, just evidence that (a) the problem is reproducible and fixable with a small, well-scoped rule, and (b) at least two independent parties (ntop/nDPI and us) converged on excluding the same three extensions.
Problem
JA4 was designed to be both unique and stable for a given TLS client implementation. The current spec, however, does not define how to treat extensions whose presence/content varies per-connection for reasons unrelated to client identity (session resumption, ticket rotation, padding length). This means:
- Different parties invent their own ad-hoc fix (ntop/nDPI, and separately us against fingerproxy), fragmenting the ecosystem instead of having one canonical answer.
- Anyone relying on JA4 for fingerprint-based grouping/detection (one of JA4's stated use cases) gets false negatives when a stable client appears as multiple distinct fingerprints across resumed sessions.
Proposal
We'd like to propose a new JA4+ method — a connection-stable variant of JA4 that normalizes or excludes a well-defined set of "ephemeral" TLS extensions before hashing, so the same client/library produces the same fingerprint across session-resumption events.
Conceptually this would sit alongside JA4 the same way JA4S sits alongside JA4 today: same family, distinct, explicitly-scoped purpose. We are not proposing to change JA4 itself — backward compatibility and existing detections built on JA4 should be unaffected.
Open questions we'd like FoxIO's input on, since JA4+ naming/spec is maintained by FoxIO:
- Naming: JA4S is already taken (TLS Server fingerprinting), so we can't reuse "stable" => JA4S as we originally considered. We're tentatively suggesting JA4E ("ephemeral-extension-invariant"), but defer to your judgement on the letter/naming convention.
- Scope of "ephemeral": should the initial set match nDPI's and our fingerproxy PR's (session_ticket / RFC 9149, pre_shared_key / RFC 8446, padding / RFC 7685), or should it be extensible/versioned as new ephemeral extensions appear in future TLS revisions?
- Relationship to JA4 vs JA5: should this be a JA4+ sibling method, or fold into whatever JA5 ends up being? Happy to align with whatever direction FoxIO is already planning.
We are raising this as a specification proposal, not a documentation PR, since JA4+ naming and the formal definition of each method are maintained by FoxIO and subject to licensing/trademark considerations (per the JA4+ README licensing section). Once there's agreement on the name and the precise extension list/algorithm, we're happy to help with spec docs and/or a reference implementation.
References
Context
About six months ago (Jan 2, 2026) ntop published an analysis ("Is JA4 Now Obsolete?", https://www.ntop.org/is-ja4-now-obsolete/) showing that JA4 can produce different fingerprints for the same client across consecutive connections. Their reproduction used Safari as the example: Safari uses the TLS 1.3 pre-shared key extension (RFC 8446) to resume a previous session, and the presence/parameters of that extension (along with related "ephemeral" extensions like session_ticket and padding) change the JA4_b hash even though the underlying client/library is unchanged.
We want to flag that Safari is just the example ntop happened to use — we've independently observed the same instability (fresh handshake vs. resumed handshake producing different JA4 values for the same client) across several different browsers, not just Safari. This isn't a Safari-specific quirk, it's a structural property of how JA4 treats connection-state-dependent extensions.
ntop's own mitigation in nDPI is an opt-in flag (
tls,metadata.ja_ignore_ephemeral_tls_extn) that strips a defined set of "ephemeral" extensions (session ticket, pre-shared key, padding) before computing JA4, restoring a stable fingerprint for the same client across reconnects. Their writeup explicitly frames this as a stopgap "until a JA5 standard is published."Prior art / existing implementation
We ran into this same instability problem and needed a stable JA4, so we already implemented and upstreamed a fix as a PR to the
fingerproxyGo library:wi1dcard/fingerproxy#42
Summary of that implementation, for reference:
StableJA4Fingerprintalongside the existing raw JA4, leaving raw JA4 behavior unchanged.padding(0x0015),session_ticket(0x0023),pre_shared_key(0x0029) — the same set ntop identifies as ephemeral.This isn't meant as "the" reference implementation, just evidence that (a) the problem is reproducible and fixable with a small, well-scoped rule, and (b) at least two independent parties (ntop/nDPI and us) converged on excluding the same three extensions.
Problem
JA4 was designed to be both unique and stable for a given TLS client implementation. The current spec, however, does not define how to treat extensions whose presence/content varies per-connection for reasons unrelated to client identity (session resumption, ticket rotation, padding length). This means:
Proposal
We'd like to propose a new JA4+ method — a connection-stable variant of JA4 that normalizes or excludes a well-defined set of "ephemeral" TLS extensions before hashing, so the same client/library produces the same fingerprint across session-resumption events.
Conceptually this would sit alongside JA4 the same way JA4S sits alongside JA4 today: same family, distinct, explicitly-scoped purpose. We are not proposing to change JA4 itself — backward compatibility and existing detections built on JA4 should be unaffected.
Open questions we'd like FoxIO's input on, since JA4+ naming/spec is maintained by FoxIO:
We are raising this as a specification proposal, not a documentation PR, since JA4+ naming and the formal definition of each method are maintained by FoxIO and subject to licensing/trademark considerations (per the JA4+ README licensing section). Once there's agreement on the name and the precise extension list/algorithm, we're happy to help with spec docs and/or a reference implementation.
References