ChatGPT Won't Let You Type Until Cloudflare Reads Your React State
TL;DR Highlight
A reverse-engineering analysis that decrypts Cloudflare Turnstile's encrypted bytecode to confirm that it inspects not only browser fingerprints but also React app internal state (such as __reactRouterContext) before ChatGPT allows a message to be sent.
Who Should Read
Security developers curious about how bot detection/prevention systems work, or developers building automation tools or scrapers who want to understand the internal workings of Cloudflare Turnstile.
Core Mechanics
- Every time ChatGPT sends a message, it silently runs a Cloudflare Turnstile program in the browser. This program is delivered as freshly encrypted bytecode (28,000-character base64) per request, and user input is blocked until this check completes.
- The encryption is two-layered. The outer layer is XOR-decrypted using the p token from the prepare request, and the inner 19KB blob is XOR-decrypted using a key embedded as a float literal (e.g., 97.35) inside the bytecode. Because the key is included within the payload, full decryption is possible with only the HTTP request/response. The author verified this across all 50 of 50 requests tested.
- The decrypted program is written for a custom VM with 28 opcodes, and register addresses are randomized per request. The author decompiled the Cloudflare SDK source (sdk.js, 1,411 lines) to reverse-engineer the opcodes.
- The 55 attributes collected by the program are divided into three layers. Layer 1 is browser fingerprinting (8 WebGL, 8 screen, 5 hardware, 4 font measurement, 8 DOM, 5 storage attributes). Layer 2 consists of 5 Cloudflare edge headers (city, latitude/longitude, IP, region, etc.) that only exist on requests routed through the Cloudflare network.
- The most notable Layer 3 is a React app state inspection. It checks three values: __reactRouterContext (an internal object attached to the DOM by React Router v6+), loaderData (the result of route loaders), and clientBootstrap (a ChatGPT-specific SSR hydration object). These values only exist if the JavaScript bundle has actually executed and React has fully hydrated.
- As a result, bots that only emulate browser APIs or parse HTML cannot reproduce React internal state and are detected. The author describes this as bot detection at the 'application layer' rather than the browser layer.
- The collected fingerprint is stored in localStorage under the key '6f376b6560133c2c' and persists across page loads. The program concludes with 4 VM instructions that serialize all 55 attributes into JSON to generate a token.
Evidence
- "Nick, an OpenAI Integrity team employee, commented directly on the post, explaining that this check is intended to prevent platform abuse such as bots, scraping, and fraud, and that it aims to concentrate GPU resources on real users to sustain free unauthenticated usage. He also noted that page load time, time-to-first-token, and payload size are monitored, and that the impact is negligible for most users with only a small minority experiencing slight delays. | The author's claim that 'React app state only exists after full hydration' is key to bot detection was countered with the argument 'Does this matter if headless browsers also execute JavaScript?' Since real Chrome headless does run JS bundles, some argued the real purpose is closer to filtering lightweight bot frameworks that only stub browser APIs. | Some noted that running a full Windows 11 VM with Chrome and GPU acceleration enabled would bypass all these checks, and that at approximately $0.01 per 1,000 page loads on AWS, it is affordable enough to be practical. Specific figures were mentioned suggesting that memory page deduplication could allow 50 VMs to run simultaneously. | User experience complaints about Cloudflare were also raised, including excessive CAPTCHA encounters when using Firefox or IPs deemed 'suspicious,' and one user reported ChatGPT tabs freezing after long conversations even on an M5 MacBook Pro. | The 'So what?' problem of the article was also pointed out. Many reacted that OpenAI simply wants to serve users who properly run their React app, questioning what the issue is. While the reverse engineering itself was found interesting, many felt the practical conclusion was unclear, as illustrated by comments like 'Does this enable free API inference?'"
How to Apply
- "If you are building a bot detection system for your own service, adding an application-layer check that sends React app state (__reactRouterContext, loaderData, and other hydration artifacts) to the server to verify that a real SPA has executed can effectively filter lightweight bots that only emulate browser APIs. | If you are considering adopting Cloudflare Turnstile, this analysis helps you understand that Turnstile is not a simple CAPTCHA but a VM-based fingerprinting system that collects 55 attributes, allowing you to weigh user latency and privacy trade-offs before making an adoption decision. | If you need to test ChatGPT or Turnstile-protected sites in automated or E2E test environments, you should run a real Chromium instance with GPU rendering and allow React hydration to complete, rather than using API stubs or lightweight headless mode, in order to pass Turnstile checks. | For security research purposes, if you want to analyze Turnstile bytecode, you can reference the author's publicly shared 5-step decryption chain (p token XOR → float key extraction → inner blob decryption) to fully decrypt the program from HTTP traffic capture alone."
Code Example
# Turnstile outer layer decryption code shared by the author
import base64, json
# Step 1: Read p_token from the prepare request and dx from the response
# Step 2: Extract the outer bytecode (89 VM instructions) via XOR decryption
outer = json.loads(bytes(
base64.b64decode(dx)[i] ^ p_token[i % len(p_token)]
for i in range(len(base64.b64decode(dx)))
))
# → Array of 89 VM instructions
# Step 3: Find the 5-arg instruction after the inner 19KB blob and use the last argument (float) as the XOR key
# Example: [41.02, 0.3, 22.58, 12.96, 97.35] → key is 97.35
# Step 4: XOR-decrypt the inner blob using str(key) → JSON containing 417–580 VM instructions
inner = json.loads(bytes(
base64.b64decode(blob)[i] ^ ord(str(key)[i % len(str(key))])
for i in range(len(base64.b64decode(blob)))
))
# React app state attributes checked by Turnstile (Layer 3)
# - window.__reactRouterContext (React Router v6+ internal DOM object)
# - loaderData (result of route loaders)
# - clientBootstrap (ChatGPT SSR hydration-specific object)
# Key under which the fingerprint is stored in localStorage
FINGERPRINT_KEY = '6f376b6560133c2c'Terminology
Related Papers
Show HN: Mindwalk – Replay coding-agent sessions on a 3D map of your codebase
Claude Code나 Codex 같은 AI 코딩 에이전트가 세션 중 코드베이스의 어떤 파일을 탐색하고 수정했는지를 3D 지도 형태로 시각화해서 재생해주는 로컬 도구다. 에이전트가 작업을 어떻게 이해했는지 한눈에 파악할 수 있다.
Ghost Font: A font that humans can read but AI cannot
움직임(모션)을 이용해 글자를 표현해서 AI 모델이 정적 이미지 분석으로는 메시지를 해독하지 못하게 막는 실험적 프로젝트인데, 커뮤니티에서는 이미 GPT-5.6, Claude Opus 등으로 해독에 성공한 사례가 속출해 실효성 논쟁이 뜨겁다.
GPT-5.6, Grok 4.5, Claude, and Muse Spark build the same 4 apps
12개 LLM 모델에게 레이캐스터 미로, 루빅스 큐브, 계산기, Game of Life 앱을 각각 5번씩 만들게 해서 성공률·비용·속도를 비교한 실전 벤치마크다. GPT-5.6 Sol이 전반적으로 가장 일관된 결과를 냈고, Grok 4.5는 가성비 면에서 눈에 띄었다.
Benchmarking coding agents on Databricks' multi-million line codebase
Databricks가 자사 실제 코드베이스를 기반으로 여러 AI 코딩 에이전트의 성능과 비용을 직접 측정했고, 모델 토큰 가격과 실제 태스크 비용이 전혀 다르다는 점, 그리고 오픈소스 모델이 이제 최상위 수준에 도달했다는 점을 확인했다.
Estimating Uncertainty from Reasoning: A Large-Scale Study of Multi- and Crosslingual MCQA Performance in LLMs
LLM이 저자원 언어 질문을 받을 때 영어로 추론하게 하면 불확실성 추정 성능이 고자원 언어 수준으로 올라온다.
LLM-as-a-Verifier: A General-Purpose Verification Framework
LLM의 토큰 확률 분포를 활용해 discrete 점수 대신 continuous 점수를 뽑아내면, 추가 학습 없이 코딩·로봇·의료 에이전트 평가 정확도를 SOTA로 끌어올릴 수 있다.