Open ChatGPT in a browser. Watch the network tab. Before you can type a single character, your browser silently downloads a base64-encoded blob from Cloudflare, decrypts it into a small virtual machine, and runs 89 instructions that walk through your GPU, your screen, your fonts, your IP region — and then dives directly into the React internals of the page itself, looking for properties like __reactRouterContext and loaderData.
If the program likes what it sees, the input field unlocks. If it doesn’t, you get a challenge.
A security researcher recently captured 377 of these programs from ChatGPT’s network traffic and decrypted them. The result is the clearest public look at how Cloudflare Turnstile actually decides who is a bot.
The three layers it inspects
Across the 377 sampled programs, the inspection checks 55 properties spread across three layers. None of them are obviously visible to a normal user.
Layer 1 — your browser
The traditional fingerprinting surface, but more thorough than the open-source libraries you might be familiar with:
- WebGL — 8 distinct properties, including renderer name, vendor, supported extensions, and parameters that vary subtly between real GPUs and headless emulators.
- Screen — 8 properties: dimensions, color depth, pixel ratio, available area. Headless browsers tend to round these in detectable ways.
- Hardware — 5 properties:
navigator.hardwareConcurrency, memory hints, deviceMemory, platform. - Font measurement — 4 properties: it actually measures the rendered width of test strings against installed fonts. Fingerprint-resistant browsers like Brave normalise this; the program detects the normalisation.
Layer 2 — Cloudflare’s edge
The interesting part is what happens server-side during the challenge. Cloudflare’s edge already knows things about you that no client-side script can lie about:
- Your IP address and the city/region it geolocates to.
- Your AS number and reputation, derived from prior Cloudflare-network traffic.
- The geographic edge header that your TCP connection actually terminated at.
These are stamped into the challenge response. The decoded program then verifies that the client-side claims about location are consistent with what the edge already saw. Spoofing your timezone in JavaScript while connecting from a known data-centre IP is exactly the inconsistency this catches.
Layer 3 — the React application itself
This is the part that surprised people. The Turnstile program doesn’t just check that a browser exists — it checks that the actual ChatGPT React app booted correctly.
It walks the DOM looking for React’s internal hooks:
__reactRouterContext— present on a properly hydrated React Router app.loaderData— populated by React Router data loaders during real navigation.clientBootstrap— set by ChatGPT’s own client-side bootstrapping code.
If you opened ChatGPT in a headless Puppeteer instance and the React tree didn’t fully hydrate — or you tried to bypass the page by hitting the API directly while faking a session — these properties are missing or malformed, and the challenge fails. The bot has to actually run the app.
This is a meaningful escalation. Past bot-detection logic could be fooled by a sufficiently realistic Chrome profile. This logic requires that the target application’s own internal state machine has run to completion.
How the decryption works
The 377 programs all share the same envelope, and the researcher reconstructed it:
- Cloudflare sends
turnstile.dxin the prepare response — about 28,000 characters of base64 that change on every request. - Decoding gives an outer layer that has been XOR’d with a
ptoken from the same response. - After the XOR, the outer layer becomes 89 VM instructions for a small custom bytecode interpreter.
- Those 89 instructions walk a 19 KB inner encrypted blob.
- The inner blob’s decryption key is a float literal embedded directly in the bytecode — generated server-side per request.
So three things change on every request: the outer XOR token, the bytecode instructions themselves, and the inner-blob key. The actual property checks are stable, but the wrapping makes static analysis useless. You have to execute the program to know what it’s checking.
This is what makes the system robust against script-based bypass attempts: there is no fixed set of “anti-bot fields” you can pre-compute. The challenge mutates per-request.
Why this is worth understanding
Three concrete takeaways for developers:
1. Bot detection now reaches into your app’s internal state
If you ship a React or Next.js app behind Cloudflare’s bot management, the verifier may expect certain framework-internal properties to exist on the page. Build pipelines that strip or rename those (aggressive minification, custom React forks, server-only rendering) can make legitimate users look like bots.
2. Headless browsing for testing is now genuinely hard
Puppeteer and Playwright against a Cloudflare-protected app are no longer just about user-agent strings. You need a real GPU to pass WebGL, fonts that haven’t been normalised, network egress from a residential-looking IP, and the target application to fully hydrate. End-to-end tests against production sites need a different strategy — often a parallel testing endpoint that bypasses Turnstile.
3. Privacy implications are real
The combination of WebGL, screen, hardware, fonts, and Cloudflare’s edge data is a high-entropy fingerprint. Across hundreds of millions of users, two visitors with identical 55-property values are extremely rare. Cloudflare doesn’t need a cookie to recognise you — and now it has access to the React internals of every site that opts in.
This is the standard tradeoff: better bot mitigation, less anonymity. Whether you find that tradeoff worthwhile depends on whether you’re a publisher fighting scraping or a user who values not being uniquely tracked across the open web.
What this changes
For two years the bot-mitigation arms race was mostly about JavaScript fingerprinting libraries on the client. The 377 decrypted Turnstile programs show the front has moved: the verifier is now a server-generated, per-request bytecode VM that introspects the application’s own runtime state. It is harder to bypass, harder to study, and harder to debug when it goes wrong.
If you build behind Cloudflare, it pays to know what it’s doing under the hood — both so you don’t accidentally trip it, and so you understand what your users are actually loading every time they visit your site.
