Cloudflare Turnstile Error Codes: What They Mean and How to Fix Them

300030, 600010, 110200 and the rest — what each Cloudflare Turnstile error code actually means in your setup, and the fix that usually works.

You wired up Turnstile, the widget renders, and then the console throws 300030 at you. Or 600010. Or a bare [Cloudflare Turnstile] Error: 110200 with no explanation. Cloudflare's own docs list the codes but rarely tell you what actually went wrong in your setup.

Here's what each family of codes means in practice, and the fix that usually works. I've grouped them by the part of the flow that failed, because the number itself tells you less than where it fired.

The quick lookup

CodeWhat it meansFirst thing to check
300030 / 300xxxGeneric challenge execution failureWidget config, sitekey/domain match
110200Domain not allowed for this sitekeyThe hostname in your Turnstile dashboard
110100 / 110110Invalid or unknown sitekeyYou're using the wrong key, or a secret key as a sitekey
400010Invalid parameters passed to the widgetYour render() options
600010Challenge solved but the browser looks automatedYour automation fingerprint
600xxxVisitor-side execution / interactivity failureNetwork, headless flags, timing

110200: domain not allowed

This one is the most common and the easiest to misdiagnose. Turnstile binds every sitekey to a list of hostnames you set in the Cloudflare dashboard. If the page serving the widget is on a hostname that isn't on that list, you get 110200 and the widget refuses to run.

Where people trip: testing on localhost when the key is registered for example.com. Or running on staging.example.com when only the apex is allowed. Cloudflare does include localhost automatically for testing keys, but not for production keys.

Fix: open the Turnstile widget settings, check the Hostname Management list, and add whatever hostname your browser's address bar actually shows. Subdomains are not wildcarded unless you add them.

110100 / 110110: invalid sitekey

You're passing something that isn't a valid sitekey. The usual culprit is grabbing the secret key (the server-side one, starts with 0x4AAAA... too, which makes it easy to confuse) and dropping it into the client widget. The sitekey is public and goes in the browser; the secret key is private and only ever touches your backend for verification.

If you're not sure which key the page is even using, that's its own problem. We wrote a short guide on finding a Turnstile sitekey on any page.

400010: bad widget parameters

Something in your turnstile.render() call is malformed. An action string with disallowed characters, a cData value that's too long, a callback that isn't a function. The widget validates its inputs and bails with 400010 rather than silently ignoring them.

Read the options back carefully. action must be alphanumeric plus underscores and dashes, 32 characters max. cData has similar limits. If you're generating either dynamically, log the exact string you're passing before render() runs.

300030 and the 300 family: execution failed

These are the vague ones. The challenge started, tried to run, and something in its own execution broke. In a real browser this is often transient: a flaky network, an ad blocker or privacy extension nuking the challenge script, or the challenges.cloudflare.com request getting blocked by a corporate proxy.

Reload once. If it clears, it was transient. If it fires every time on the same machine, look at what's sitting between the browser and Cloudflare: extensions, a VPN, a filtering DNS resolver. The challenge needs to load its script and phone home, and anything that interferes shows up as a 300.

600010: solved, but you look like a bot

This is the interesting one, and the one that sends people here. The challenge ran, the proof-of-work completed, and Cloudflare still decided the client was automated. You'll see it when you're driving a headless browser, a stealth-patched Chromium, or a plain HTTP client pretending to be a browser.

Turnstile doesn't just check a box. It fingerprints the runtime: the JavaScript engine's quirks, the exact TLS handshake, canvas and WebGL signatures, timing of interactions, and whether the automation flags that Selenium and Puppeteer leave behind are present. When enough of those say "not a human in a normal browser," you get 600010 even though the math checked out.

The fixes, in order of effort:

  • Strip the obvious automation tells (the navigator.webdriver flag, the CDP artifacts, mismatched user-agent vs TLS).
  • Use residential or mobile IPs instead of datacenter ranges, which Cloudflare scores harder. We go deep on residential vs datacenter proxies elsewhere.
  • Match your TLS fingerprint to the user-agent you claim to be. A Chrome UA with a Python TLS signature is an instant flag.

At a certain point the maintenance cost of keeping a stealth browser ahead of Cloudflare's fingerprinting is higher than the work itself. That's the trade-off that pushes most people toward a solving API: you send the sitekey and page URL, something else keeps the fingerprint current, and you get a valid cf-turnstile-response token back. For what it's worth, we run around 8,000 Turnstile solves a minute, and the single biggest cause of failures we see isn't the challenge itself, it's customers' own datacenter proxies getting scored down. The 600 family is almost always an IP or fingerprint story.

Verifying the token is a separate step

One more thing worth saying, because it generates its own round of confusion. Getting a token in the browser is only half the job. Your backend still has to POST that token to siteverify with your secret key, and that call returns its own set of error codes in the error-codes array: missing-input-response, invalid-input-response, timeout-or-duplicate.

timeout-or-duplicate catches a lot of people. A Turnstile token is single-use and expires after 300 seconds. If you solve it, cache it, and try to verify the same token twice, or verify it five minutes later, Cloudflare rejects it. Solve, verify, move on, all within the window.

When the error is really "this is too much work"

If you're hitting Turnstile on a handful of pages for QA or a small scraper, work through the codes above and you'll clear most of them. If you're hitting it at volume and spending your week chasing 600010 across a rotating fleet, the error codes are a symptom of a bigger problem: keeping a browser fingerprint and an IP pool clean enough to pass is a full-time job, and Cloudflare changes the target regularly.

That's the case Peak exists for. One API call with the sitekey and URL, a token back in about a second, and you pay only for solves that actually work. See what it costs, or read the full developer guide if you want the request and response shape first.