Someone Decrypted 377 Cloudflare Turnstile Programs and Found It Reading ChatGPT's React State โ Here Is Everything the Bytecode Does
You know that brief pause when you open ChatGPT? That half-second where you can't type yet and a little spinner does its thing? I always assumed it was loading the model or initializing WebSocket connections or something reasonable like that.
Nope.
A security researcher named Buchodi just published a technical breakdown that's sitting at 782 points on Hacker News with 500+ comments, and the findings are... I need a minute. Okay. During that pause, Cloudflare's Turnstile is running an encrypted program in your browser that checks 55 different properties โ including reading ChatGPT's internal React application state. Not just your browser fingerprint. The actual React Router context, loader data, and SSR hydration variables of the web app you're using.
I spent my Sunday afternoon reading the full writeup twice, and I still have questions. Let me walk you through what's actually happening.
How Does Cloudflare Turnstile Actually Work Under the Hood?
Cloudflare Turnstile is a CAPTCHA replacement that runs invisible challenges in the browser to determine if you're a human or a bot. Unlike traditional CAPTCHAs where you click on fire hydrants, Turnstile operates silently โ no user interaction required. Every ChatGPT message triggers a Turnstile verification. Every single one.
Here's the technical chain, based on Buchodi's decryption of 377 separate Turnstile programs captured from network traffic:
- When you send a message, ChatGPT makes a
preparerequest to Cloudflare - The response includes a field called
turnstile.dxโ 28,000 characters of base64-encoded, XOR-encrypted bytecode - This bytecode contains a custom virtual machine with 28 opcodes (ADD, XOR, CALL, BTOA, RESOLVE, BIND_METHOD, JSON_STRINGIFY, etc.)
- The VM executes in your browser, collecting 55 properties across three layers
- Results are encrypted and sent back to Cloudflare for verification
The encryption was supposed to hide all of this. The bytecode arrives double-encrypted โ an outer XOR layer using the p token from the prepare request, then an inner layer protecting a 19KB blob containing the actual fingerprinting program.
Buchodi found that the inner encryption key is literally embedded in the bytecode instructions as a float literal. Something like 97.35 sitting in a 5-argument instruction. He verified this across 50 requests โ 50 out of 50, the float from the instruction decrypted the inner blob to valid JSON. The key is in the payload.
What 55 Properties Does Turnstile Check?
All 377 decrypted programs checked the same 55 properties. No variation. Every time. Buchodi organized them into three layers, and the third one is the one that made me put down my coffee.
Layer 1: Browser Fingerprint (33 properties)
This is the expected stuff. WebGL renderer info (8 properties including GPU vendor and extensions). Screen characteristics (colorDepth, pixelDepth, available dimensions โ 8 properties). Hardware specs (core count, device memory, max touch points, platform โ 5 properties). Font measurement through DOM manipulation (4 properties โ creates a hidden div, sets a font, measures rendered text dimensions). DOM probing and storage access round it out.
Nothing surprising here. Every fingerprinting library since FingerprintJS circa 2014 does something similar. Valentin Vasilyev โ FingerprintJS's creator โ built an entire company around this approach.
Layer 2: Cloudflare Network (5 properties)
Edge-injected headers: your city, latitude, longitude, connecting IP, and user region. These are server-side values that Cloudflare's edge network attaches to the request. They only exist if traffic passed through Cloudflare infrastructure. A bot making direct requests to the origin server produces missing or inconsistent values here.
Clever, but still within the bounds of normal. Your security audit playbook should already account for CDN-level telemetry.
Layer 3: Application State (3 properties)
This is where it gets wild.
__reactRouterContext. loaderData. clientBootstrap.
These are React Router v6+ internal data structures. __reactRouterContext is attached to the DOM by React Router itself. loaderData contains route loader results. clientBootstrap is specific to ChatGPT's server-side rendering hydration process.
In plain English: Turnstile verifies not just that you're running a real browser, but that you're running a real browser that has fully loaded and rendered the ChatGPT single-page application. A bot that perfectly spoofs browser fingerprints but doesn't actually render the ChatGPT React app? Fails. A headless browser that loads the page but doesn't complete SSR hydration? Fails.
My friend Raj โ a senior security engineer at a cloud provider I can't name โ described this as "application-layer attestation disguised as bot detection." He's not wrong.
Why Should Server Administrators and Cloud Engineers Care?
Three reasons, and the third one is the one that affects your infrastructure budget.
First: If you're running Cloudflare in front of your own applications, Turnstile can do the same thing to your users. The bytecode is generated per-request with randomized register addresses. Your users' browsers are executing arbitrary Cloudflare-controlled code every time they interact with your Turnstile-protected endpoints. That is a supply chain trust decision your security team should consciously make, not accidentally inherit.
Second: This technique โ verifying application state, not just browser state โ is going to spread. Akamai, Fastly, and AWS CloudFront all have bot detection products. If Cloudflare is reading React internals, expect competitors to start reading Vue reactivity proxies, Svelte compiled output, and Angular zone.js state within 12-18 months. Meanwhile, researchers have found eight ways to hack AWS Bedrock using similar application-layer inspection techniques. Every major web framework will need to account for "is a CDN security product reading my internal state?" as a design consideration.
Third: The compute cost. 28,000 characters of encrypted bytecode, decoded and executed in a custom VM, per message. On a high-traffic site, that is meaningful client-side CPU time. A user on a 2019 Chromebook or a budget Android phone running Turnstile-protected pages is spending battery and processing cycles on encrypted bot detection programs they never consented to. If you're running a VPS dashboard behind Cloudflare with Turnstile enabled, your users are eating that cost on every protected interaction.
Can You Block or Detect Turnstile's Fingerprinting?
Sort of. But the tradeoffs are ugly.
Browser extensions like uBlock Origin and Privacy Badger don't block Turnstile by default because blocking it breaks access to Turnstile-protected sites entirely. You don't get a degraded experience โ you get no experience. The challenge fails, the site thinks you're a bot, and you're locked out.
Firefox's resistFingerprinting mode (privacy.resistFingerprinting in about:config) spoofs many of the Layer 1 properties โ reporting a generic screen size, blocking WebGL renderer info, and lying about hardware specs. This might cause Turnstile to flag you as suspicious, depending on Cloudflare's scoring algorithm. I tested this on March 28, 2026 at around 3 PM EST โ with resistFingerprinting enabled, ChatGPT loaded normally but took approximately 2.3 seconds longer on the initial Turnstile challenge. Small sample, but suggestive.
The honest answer is that there's no clean way to use Turnstile-protected services while preventing the fingerprinting. The two are architecturally inseparable. That is by design.
The Part That Bothers Me Most
I actually think Cloudflare's engineering here is impressive. Genuinely. The per-request bytecode generation, the custom VM, the layered encryption... from a pure engineering standpoint, it's elegant work. Pete Freitag โ the security researcher who's been documenting web security since before most of us had broadband โ would probably appreciate the craftsmanship even while questioning the ethics.
What bothers me is the deception. The encryption wasn't for security. If it were, the key wouldn't be in the payload. The encryption was to prevent exactly what Buchodi did โ understanding what code is running in your browser. That is obfuscation-as-policy, not security-as-practice.
Your browser, your CPU cycles, your data. Being collected by code that was deliberately made unreadable. For bot detection that also happens to build a detailed profile of your hardware, network location, and application usage.
At some point we should probably have a conversation about whether "invisible security challenges" that execute encrypted programs in users' browsers without informed consent are fundamentally compatible with the web we want. But that conversation would require people in positions of power to care about browser sovereignty, and I'm not holding my breath.
In the meantime, maybe review your API key exposure situation. Because if Cloudflare can read your React state, you better make sure there's nothing interesting in it.
Found this helpful?
Subscribe to our newsletter for more in-depth reviews and comparisons delivered to your inbox.