A JA4 fingerprint is a client "fingerprint" extracted from the TLS handshake (ClientHello).
It is unique to each attack tool and does not change even when the IP / country / VPN / User-Agent is switched.
However, whether JA4 can be captured depends on "who receives the TLS handshake first (where TLS is terminated)".
This page lists the feasibility for each representative setup pattern.
Conclusion: it depends on where TLS is terminated
[Client] ──TLS handshake──▶ [ only whoever saw the ClientHello can compute JA4 ]
Computing JA4 requires the raw ClientHello byte sequence. A process that only sees
the plaintext HTTP after TLS decryption cannot reconstruct JA4 afterward.
JA4 feasibility by setup pattern
Computing JA4 needs the raw ClientHello bytes, so feasibility is decided solely by
where TLS is terminated (= who sees the client's ClientHello first) and
unmask's mode (native / forward-auth). The backend app is irrelevant.
● = node where unmask is installed ★ = TLS termination (where the ClientHello is seen)
Setup (client → …)
JA4
unmask config
1. Front LB (co-located with unmask) + backend app — unmask terminates TLS
native = nginx + unmask-plugin-nginx. The C module parses the ClientHello directly and computes JA4 (fastest, no subrequest, runs even on old distros like CentOS 6). Use this if you want JA4.
forward-auth = no plugin; verdicts delegated via auth_request / Apache mod_lua. It doesn't see TLS itself, so JA4 is empty unless the front CDN / LB forwards it (runs on UA / IP / rate / honeypot).
Apache has no native plugin (2.4's mod_ssl exposes no JA4 metadata; 2.6+ exposes metadata but unmask doesn't use it yet). For JA4 on Apache, the only path is forwarding from a front CDN / LB.
For forward-auth in rows 3–4, pick your front CDN / LB under “trusted LB / CDN” (same as native). unmask then renders a gate that adopts X-Client-JA4 only from that LB's IPs, and the shipped server.inc forwards the gated value automatically — no toggle, no manual wiring. See LB / CDN setup.
HTTP/3 (QUIC) — the q prefix
The first letter of a JA4 fingerprint names the transport the ClientHello arrived over:
t = TLS over TCP, q = QUIC. The nginx plugin
computes JA4 for HTTP/3 the same way it does for TCP — when nginx is built with
--with-http_v3_module and listens on a listen … quic port,
the QUIC ClientHello passes through the same TLS-library callback the module already
hooks, so no extra configuration is needed. Verified against both TLS backends nginx
HTTP/3 builds commonly use: the OpenSSL 3.5+ native QUIC API and the quictls patch set.
The same client presents a different string over HTTP/3 than over TCP — the transport letter, the ALPN and the hello contents all change. When writing a custom JA4 rule for a site that serves HTTP/3, match both forms.
unmask's cookie rebinding already treats an HTTP/2 ↔ HTTP/3 switch by the same visitor as normal — it does not count as a fingerprint mismatch.
Behind a front CDN / LB that terminates HTTP/3 and forwards JA4, the forwarded value arrives with a q prefix; the trusted-LB gate passes it through unchanged.
What happens if JA4 cannot be captured?
Even with an empty JA4, unmask still works. Bot detection continues with the following signals:
User-Agent classification — identifies known bots via a crawler / bot UA list of 600+ patterns
Official IP ranges — registers official ranges (Googlebot / Bingbot etc.) on the bypass list to avoid ranking accidents. Vendors that publish ranges are verified by IP, not UA, so a spoofed UA is caught (crawlers without a range pass on UA)
rate-limit — escalates to a challenge when the per-IP × path threshold is exceeded
honeypot — bans clients that hit registered trap paths (e.g. CMS paths your site doesn't run)
behavioral CAPTCHA — a human-likeness score across 5 axes: mouseTrail / scroll / keyboard etc.
What you lose without JA4 is detection accuracy for stealth bots that impersonate legitimate browsers.
A bot that cleanly fakes both its UA and IP can only be spotted by its TLS quirks (JA4).
Conversely, as long as you can capture JA4, you can track the same tool through IP rotation and UA spoofing.
Summary — which setup to choose
Want to use JA4 fully → nginx + unmask-plugin-nginx (native mode). nginx must be terminating TLS