Defeat IP Rotation: Block Bots by JA4 at the WAF
WebDecoy now tracks bots as persistent actors and pushes a JA4 rule to your AWS WAF or Cloudflare, blocking rotating scrapers across every IP they use.
WebDecoy Team
WebDecoy Security Team
Defeat IP Rotation: Block Bots by JA4 at the WAF
Every IP ban you have ever written has the same expiration date: the attacker’s next request.
Modern scraping and credential-stuffing operations do not run from one server anymore. They run through residential proxy networks that resell access to millions of real ISP-assigned addresses, rotating to a fresh IP on every single request. Block one, and the next request arrives from a different city on a different carrier. Your rate limiter’s counter resets. Your ban list never catches up. Your dashboard shows a scatter of thousands of “unrelated” one-hit visitors that are, in reality, a single adversary wearing a new face each time.
IP-based blocking is not just weak against this. It is structurally the wrong unit of defense. You are trying to ban a location when your problem is an actor.
Today we are shipping the answer. WebDecoy now identifies bots as persistent actors built on composite fingerprints, and when an actor is confirmed to be rotating IPs, WebDecoy pushes a JA4 rule directly into your WAF — AWS WAF or Cloudflare — so the block follows the adversary across every IP it will ever use. Not the IP. The identity.
Here is how it works, and why it changes the economics of the fight.
The problem: you are banning shadows
Let’s be precise about why IP blocking fails, because the failure mode is specific.
A residential proxy provider like the ones marketed to “web scraping” and “data collection” customers gives an operator a pool of anywhere from hundreds of thousands to tens of millions of IP addresses, sourced from real consumer devices. The operator’s scraper makes a request, the proxy assigns it a random exit IP, and the next request draws a new one. From your server’s perspective:
- Rate limiting is defeated. Your limiter keys its bucket on the source IP. If every request has a new IP, every request starts with a full budget. There is nothing to throttle.
- Bans are defeated. You block
203.0.113.7. The attacker never uses it again anyway. Your ban list grows without ever intersecting live traffic. - Correlation is defeated. Your analytics can’t tell that 40,000 requests from 40,000 IPs were the same Python script. Each shows up as a lonely, low-volume visitor that no threshold will ever flag.
This is the core asymmetry: an IP address is cheap for the attacker to change and expensive for you to reason about. You need a unit of identity with the opposite property — something cheap for you to observe and expensive for the attacker to change.
That unit exists. It’s in the TLS handshake.
IP blocking vs. JA4 blocking at a glance
| Blocking by IP | Blocking by JA4 | |
|---|---|---|
| What it identifies | Where a request exits the network | The client’s TLS stack — the tool itself |
| Survives IP rotation | ❌ Resets on every new proxy IP | ✅ The handshake is identical across IPs |
| Works for no-JS bots | ✅ | ✅ (it sits below the application layer) |
| Cost for the attacker to evade | Trivial — rent another residential IP | High — rebuild on a different TLS library |
| Where it’s enforced | An IP allow/deny list | A JA4 rule in your WAF (AWS / Cloudflare) |
| Collateral risk | Low per IP, but the list never ends | Contained — WebDecoy never blocks a browser-like JA4 |
The reframe: identify the actor, not the address
When a client opens an HTTPS connection, the very first thing it sends — before any HTTP request, before any proxy rewriting — is a TLS ClientHello. That message advertises the exact cipher suites, extensions, elliptic curves, and ALPN protocols the client’s TLS stack supports, in the client’s own particular way.
That handshake is a property of the tool, not the network. A Python requests script produces a requests-shaped ClientHello whether it exits through a datacenter in Virginia or a residential IP in Jakarta. Route it through a million proxies and the handshake does not change, because the proxy forwards bytes — it does not re-negotiate TLS on the client’s behalf.
JA4 turns that handshake into a compact, stable fingerprint. It is the modern successor to JA3, and it is the network-tier identity WebDecoy uses to recognize an actor no matter how many IPs it hides behind. We wrote the full technical reference in JA4 Fingerprinting: Detect AI Scrapers by TLS — this post is about what we now do with it.
Why JA4, not JA3
If you have tried TLS fingerprinting before and found it noisy, you were probably fighting JA3’s biggest weakness. JA3 hashes the cipher and extension lists in the order the client sent them. Chrome, Firefox, and Safari now deliberately randomize that order on every connection (a technique called GREASE, plus extension shuffling), so a single browser can emit a different JA3 hash on every page load. Attackers exploit the same trick to churn their JA3 on purpose.
JA4 fixes this at the format level: it sorts the cipher and extension lists before hashing and breaks the fingerprint into readable components (TLS version, SNI presence, cipher count, extension count, ALPN). The result is stable across randomized handshakes and far harder to perturb. WebDecoy computes spec-compliant JA4 from the raw ClientHello, captured at the TLS layer and verified against the official FoxIO test vectors — not an approximation.
A JA4 looks like this:
t13d1516h2_8daaf6152771_02713d6af862The readable prefix (t13d1516h2) already tells you a lot: TLS 1.3, SNI present, 15 ciphers, 16 extensions, HTTP/2. A real Chrome and a python-requests client produce structurally different prefixes, and no amount of IP rotation changes that.
The actor model: one adversary, one identity
JA4 is the coarse, always-available layer of a bigger idea. WebDecoy now builds a composite fingerprint across three tiers, and correlates every detection to a persistent actor keyed by the strongest tier available:
| Tier | Built from | Survives IP rotation? | Notes |
|---|---|---|---|
| Network | JA4/JA3 + request header order + Accept-Language / Accept-Encoding | Yes — and works even for bots that never run JavaScript | Coarse; many users can share one value, so it is used to rate-limit and arm the WAF, never to hard-block a browser fingerprint alone |
| Device | canvas, WebGL renderer, audio stack, fonts, hardware concurrency, platform | Yes, per browser | Requires JS execution; higher-resolution identity. Not a WAF block key — enforced at your origin via the SDK, or resolved to an IP/JA4 rule |
| Crypto | RFC 9421 Signature-Agent, Privacy Pass issuer | Yes | Strongest, but only present for cooperating agents — used to verify and allow them (Web Bot Auth), not to block |
An actor is a durable record — scoped to your organization, spanning all of your properties — that tracks the set of IPs, user agents, first/last seen, aggregate threat, and enforcement state behind one fingerprint. The moment a scraper’s 40,000 rotating IPs collapse onto a single JA4, they stop being 40,000 anonymous visitors and become one actor with 40,000 IPs — which is a much louder, much more actionable signal.
That reframing does two things at once:
- Enforcement can follow the adversary. The block attaches to the fingerprint, not the address, so it travels with the actor across every future IP.
- Rotation itself becomes a detection signal. “One fingerprint seen from many IPs in a short window” is not normal browsing — it is the literal shape of a proxy-rotating bot. WebDecoy escalates an actor’s threat score as its distinct-IP count climbs, so the rotation you were losing to becomes the thing that gets you caught.
The payoff: detection arms your WAF
Here is the part that matters operationally. WebDecoy is not an inline proxy. Your traffic never routes through us, and we add zero latency to your application. So how do we block an actor across every IP without sitting in the request path?
We arm the firewall you already have.
When the correlation engine confirms that an actor is a genuine rotator — one non-browser JA4 seen across many IPs, carrying a high aggregate threat score — WebDecoy writes a composite actor signature into your own WAF through your connected cloud account. The signature keys on the JA4 combined with the actor’s network identity and path scope — never a bare fingerprint that could catch legitimate users who share it:
- AWS WAF — WebDecoy manages a rule in your Web ACL that matches the JA4 fingerprint field exactly, combining every enforced fingerprint into a single rule. AWS evaluates it at the edge for you.
- Cloudflare — WebDecoy creates a firewall rule on
cf.bot_management.ja4(the Bot Management JA4 field), the same field the JA4 guide shows you how to write by hand — now maintained automatically as new rotating actors are confirmed.
From that instant, every request matching that signature is stopped at your edge, at line rate, before it reaches your origin — following the actor across the IPs it rotates through. You did not write the rule. You did not chase the IPs. Detection armed the WAF, and the WAF enforces across the rotation pool.
This is the difference between playing whack-a-mole with addresses and cutting off the tool.
And a block is the last resort, not the first move: WebDecoy defaults to a graduated ladder — allow, rate-limit, challenge, then block — and reserves outright blocks for high-confidence, honeypot-grade evidence. Where the SDK is deployed, enforcement flips again, to allowlisting proven-good sessions. The full model is in Closed-Loop Bot Enforcement in Your Cloudflare & AWS WAF.
Conservative by design: we will not break your traffic
A JA4 rule is powerful precisely because it blocks every client sharing that fingerprint — which is also exactly why it must be handled carefully. A careless JA4 block could take out a slice of real browsers. WebDecoy’s auto-enforcement is built around not doing that:
- Browser fingerprints are never auto-blocked. Before any push, WebDecoy checks whether the JA4 looks browser-like — HTTP/2 with a full cipher and extension set — and if it does, it refuses to block, full stop. Only non-browser tool fingerprints (curl,
python-requests, Go’s HTTP client, headless automation) are eligible. - Only confirmed rotators qualify. A fingerprint must be seen across a conservative floor of distinct IPs and carry a high aggregate threat score before it is even considered. A single suspicious hit never arms the WAF.
- It is opt-in. Auto-blocking fires only through a Response Action you configure — a simple when a rotating actor is confirmed, then block it at the WAF rule. Prefer to be notified instead of blocking? Point the same trigger at a webhook and route it into your own SIEM or on-call flow. Nothing is pushed to your firewall unless you asked for it.
- Everything is visible and reversible. Every rule WebDecoy creates lives in your own AWS or Cloudflare account, where you can inspect or remove it at any time.
The design principle is simple: WebDecoy hands your WAF a precise, high-confidence identity to block, and never touches a fingerprint a real person might share.
Where JA4 blocking fits — and where it doesn’t
We are honest about the limits of every layer, because pretending a single signal is a silver bullet is how detection programs fail. JA4 blocking is devastating against the huge class of bots that rotate IPs but cannot rotate their TLS stack — the scripts, SDK-based scrapers, and headless HTTP clients that make up most automated abuse by volume. It is the cheapest possible win against the most common attack.
This is also how the industry treats JA4, and it’s worth being explicit about it. Mature bot defenses — AWS, DataDome, Cloudflare — never block on a fingerprint alone; they use JA4 as one feature in a score, combined with request velocity, endpoint mix, and behavioral signals, and reserve hard blocks for high-confidence non-browser tools. WebDecoy follows the same rule with a clean division of labor: JA4 is a correlation key, and a block key only for tools — never for browsers. The per-request “is this specific Chrome session a human or a bot?” decision belongs to challenges and behavioral signals at your inline WAF or bot-manager — so that’s exactly where we send the intelligence (the actor, its breadth, its non-browser JA4, its IPs), rather than trying to make that call from a passively observed fingerprint.
It is not the answer to a scraper that drives a real Chrome to produce a genuine browser handshake. That client shares a browser-like JA4 with your actual users, so WebDecoy will not block it at the network tier — and it shouldn’t. Those adversaries are caught by the other layers:
- Tripwires and honeytokens — deterministic traps that catch a link-following scraper regardless of how perfect its fingerprint is.
- Device fingerprinting — the canvas/WebGL/audio tier that resolves a real-browser bot to a persistent device identity even when its JA4 is unremarkable.
- Why we don’t rely on ML classifiers — the philosophy behind combining deterministic layers instead of trusting one probability score.
The actor model is what ties these together: JA4 handles the network tier, device fingerprinting handles JS-executing bots, and cryptographic identity handles cooperating agents — all rolling up to one actor you can reason about and act on.
Turn it on
If you already run AWS WAF or Cloudflare, arming it with WebDecoy takes three steps:
- Connect your WAF. Add your AWS WAF or Cloudflare integration in the WebDecoy dashboard (Cloudflare JA4 rules require the Bot Management add-on).
- Create a Response Action. When a rotating actor is confirmed → block it at the WAF. Or start in report-only mode by routing the trigger to a webhook first.
- Watch the actors surface. As traffic flows, rotating adversaries collapse from thousands of anonymous IPs into a handful of named actors — and the ones you chose to block stop reaching your origin.
You have been collecting the evidence to catch these actors all along. It was scattered across thousands of IP rows where no threshold could see it. WebDecoy pulls it into one identity — and hands that identity to the firewall you already trust.
Stop banning shadows. Block the actor.
Want the deep technical background on the fingerprint itself? Read JA4 Fingerprinting: Detect AI Scrapers by TLS and the WebDecoy SDK TLS fingerprinting release. Ready to try it? Get in touch.
Frequently Asked Questions
How is blocking by JA4 different from blocking an IP address? +
An IP address describes where a request came from; a JA4 fingerprint describes what sent it. When a scraper rotates through a residential proxy pool, its IP changes on every request but its TLS ClientHello — and therefore its JA4 — stays the same, because JA4 is derived from the client's TLS stack, not its network path. Blocking the IP stops one request. Blocking the JA4 stops the tool behind all of its IPs at once.
Will a JA4 WAF rule accidentally block real users? +
It can if you block the wrong fingerprint, which is exactly why WebDecoy will not. Popular browsers share a small number of JA4 values, so WebDecoy never auto-blocks a browser-like fingerprint (HTTP/2 with a full cipher and extension set). It only arms the WAF for non-browser tool fingerprints — curl, python-requests, Go's HTTP client, headless automation — and only after an actor is a confirmed rotator across many IPs with a high threat score. The policy is deliberately conservative and opt-in.
Does WebDecoy sit inline and add latency to my site? +
No. WebDecoy is not a reverse proxy — your traffic never routes through us. Detection runs out of band, and when an actor qualifies for blocking, WebDecoy writes a JA4 rule into your own AWS WAF or Cloudflare account through their APIs. Enforcement happens at your edge, at line rate, with zero added latency to your application.
Why JA4 instead of JA3? +
JA3 hashes the ordered list of TLS cipher suites and extensions, which modern browsers deliberately shuffle on every connection (GREASE and extension randomization). That makes a JA3 hash unstable and easy to perturb. JA4 sorts those fields before hashing and separates the components, so it stays stable across randomized handshakes and is far harder for a scraper to churn. WebDecoy computes spec-compliant JA4 from the raw ClientHello, verified against the official FoxIO test vectors.
Can a scraper just change its JA4 to evade the block? +
Changing a JA4 means changing the TLS stack itself — the cipher suites, extensions, and ALPN a client negotiates — which most scraping tools cannot do without rebuilding on a different HTTP library. That is far more expensive than renting another proxy IP, which is the whole point: JA4 blocking attacks the part of the attacker's setup that is costly to change. Tools that drive a real browser to fake the handshake are caught by the device-fingerprint and tripwire layers instead.
Which WAFs can WebDecoy push JA4 rules to? +
AWS WAF and Cloudflare today. For AWS, WebDecoy manages a rule in your Web ACL that matches the JA4 fingerprint field exactly. For Cloudflare, it creates a firewall rule on cf.bot_management.ja4, which requires the Bot Management add-on. Both are driven through your own connected account, and you can see and remove every rule WebDecoy creates.
Share this post
Like this post? Share it with your friends!
Want to see WebDecoy in action?
Get a personalized demo from our team.