turnstile · error 600010

Cloudflare Turnstile error 600010: what it means and how to fix it

Error 600010 is a generic challenge failure: Turnstile ran, decided it could not verify the browser environment, and stopped. It belongs to the 600* family that Cloudflare documents as "Generic challenge failure — bot behavior detected", it is marked retryable, and Cloudflare publishes no specific meaning for the trailing 010.

What 600010 means

Cloudflare's error code reference lists no entry for 600010. It lists 600*, with a note above the table: "When an error code is marked with *, the remaining digits can vary and are for internal use." So 600010 is one instance of the 600 family, and the 010 suffix is not something you can look up.

FieldValue from Cloudflare's docs
Code600* (covers 600010)
DescriptionGeneric challenge failure
RetryYes
Troubleshooting"Bot behavior detected."

The client-side errors page explains the numbering: "the first three digits indicate the error family (such as configuration issues, network problems, or challenge failures), and the remaining digits specify the exact error within that family." Cloudflare's own sample error handler groups families 300 and 600 together and shows the message "Security check failed. Please try refreshing or using a different browser."

That matches what users see. The widget renders the failure as **Security Check Failed**, and Cloudflare's browser compatibility troubleshooter reports 600010 with "errorFamily": "Unknown", "severity": "medium", "retryable": true. Nothing in any of it narrows the cause further — 600010 tells you the challenge failed, not why.

Practical reading: 600010 is a verdict, not a diagnosis. It means "this browser session did not pass", and the reason lives in the client environment, not in your widget markup.

Where the sources disagree

The most-linked answer on the Cloudflare Community — marked as the accepted solution on a thread with 24k views, posted by an account badged "Former Cloudflare" — says: "That 6000010 is a configuration error. Maybe check that the Turnstile site key & secret key are correctly configured in the site's backend and frontend." The same reply was pasted into at least two 600010 threads, and it has propagated into third-party blog posts and issue trackers ever since.

This conflicts with the official documentation, which files 600* under generic challenge failure and bot behaviour. It is also hard to reconcile with how Turnstile works: the secret key is used only server-side against the siteverify API, so it cannot produce a client-side widget error code at all. Key and hostname problems have their own dedicated codes — 110100, 110110, 400020, 400070 for sitekey issues and 110200 for an unauthorised domain — and none of them are 600010.

In a later thread a Cloudflare Team member responded to a 600010 report with "Something in your devtools, such as a plugin or the emulator is triggering that error" and linked to the error codes page. The reporter and a second user both pushed back, saying they reproduced it with no extensions installed. Treat the configuration-error claim as unverified. Check your keys because it is cheap, but do not expect it to be the fix.

Causes that have been reproduced

These are cases where someone identified a trigger and showed the error going away when it was removed. The first four are consistent with Cloudflare's documented list of things that interfere with challenges.

TriggerWhat you seeFix
Developer tools open[Cloudflare Turnstile] Error: 600010. on reload; passes as soon as devtools are closedClose devtools when testing the real flow. Cloudflare's supported-browsers page states that developer tools "may modify browser behavior" and that device emulation "can trigger bot detection mechanisms"
Device/mobile emulationConsistent failure only in the emulated viewportTest on a real device. Cloudflare explicitly recommends this over emulation
Extension injecting content scriptsRequest to /cdn-cgi/challenge-platform/h/b/i/... never leaves the browser — status 0, all timing phases 0/-1Disable extensions one at a time. Reported for the Harper extension on both Chromium and Firefox; disabling it per-domain fixed it immediately
Hardened or forked Chromium buildFails in the fork, passes in Firefox on the same machineUpdate the browser. Ungoogled Chromium 133 produced 600010 on cobalt; the maintainer confirmed 134 resolved it
Headless browser or automation frameworkReliable 600010, often in a retry loopCloudflare lists headless Chrome/Firefox, Selenium, Puppeteer and Playwright as unsupported and states they "will be blocked by challenges"
Hostname with a trailing dot (example.com.)Console: Ignored message from wrong origin (trailing dot in FQDN), plus repeated 400s to challenges.cloudflare.comRedirect or rewrite so the page is never served from the trailing-dot form. Single community report, no Cloudflare confirmation
Transient platform failureOne 600010, then automatic recovery a few seconds later on a plain visible widgetNothing to fix client-side. One reporter measured recovery at 5.198s on a stock WordPress/Gravity Forms integration

The trailing-dot case is worth an explicit check because it is invisible in normal browsing and breaks the postMessage origin comparison between your page and the challenge iframe. Two lines in your page will tell you whether it applies to you.

js
// Trailing-dot FQDN check. Run this on the page that hosts the widget.
if (location.hostname.endsWith(".")) {
  console.warn(
    "Page served from a trailing-dot FQDN:", location.hostname,
    "- Turnstile origin matching will reject its own iframe messages."
  );
  // Send the visitor to the canonical form of the host.
  location.replace(
    location.protocol + "//" +
    location.hostname.replace(/\.+$/, "") +
    (location.port ? ":" + location.port : "") +
    location.pathname + location.search + location.hash
  );
}

If you are hitting 600010 from automation rather than from a real browser session, that is the expected outcome — Turnstile is doing its job. For automation against properties you own or are authorised to test (QA suites, CI, uptime monitoring, anti-bot testing), SolveGate returns a Turnstile token from a single POST /v1/solve with the gate, sitekey and page URL, typically in under 1.5s. It covers managed, non-interactive and invisible Turnstile widgets and Turnstile WAF challenge pages, and nothing else.

Console output that is not the cause

Two things show up in devtools and HAR captures alongside 600010 and get blamed for it. Cloudflare documents both as expected.

  • **401 on a /cdn-cgi/challenge-platform/.../pat/... request.** This is a Private Access Token request that the device or browser cannot satisfy. Cloudflare: "This response is expected and does not mean the visitor was blocked or that the widget failed." Turnstile falls back to a standard challenge.
  • **DNS lookup failures on subdomains of challenges.cloudflare.com.** Cloudflare calls these "expected and non-fatal for Turnstile's execution" and advises dropping network errors from the *.challenges.cloudflare.com wildcard while keeping visibility on the challenges.cloudflare.com apex. This matters for WebView and handler-based integrations that surface any network error as fatal.
  • **A debugger; statement pausing execution.** Multiple reporters trace an automatically-firing debugger to the Turnstile script itself; disabling breakpoints is what leaves them with the bare 600010. Cloudflare has not commented on this directly.

Diagnosing it on a live page

Turnstile's default is retry: "auto" with a retry-interval of 8000ms, which hides individual failures behind a loop. Turn retry off while you investigate, log the family separately from the full code, and put a bounded backoff in front of turnstile.reset(). Restore auto before you ship.

html
<div id="ts-widget"></div>
<p id="ts-status" role="status"></p>

<script>
  var widgetId;
  var attempts = 0;

  function onTurnstileLoad() {
    widgetId = turnstile.render("#ts-widget", {
      sitekey: "0x4AAAAAAA_your_sitekey_here",
      retry: "never", // diagnostic only; default is "auto"

      callback: function (token) {
        attempts = 0;
        document.getElementById("ts-status").textContent = "";
        // POST token to your server and validate it with siteverify.
        // Tokens are single-use and expire after 300 seconds.
      },

      "error-callback": function (errorCode) {
        var code = String(errorCode);
        var family = code.slice(0, 3);
        attempts += 1;

        console.warn(
          "turnstile error", code,
          "| family", family,
          "| attempt", attempts,
          "| ua", navigator.userAgent
        );

        if (family === "600" || family === "300") {
          // Generic challenge failure. Back off, then stop and tell the visitor.
          if (attempts <= 3) {
            setTimeout(function () { turnstile.reset(widgetId); }, 2000 * attempts);
            return true;
          }
          document.getElementById("ts-status").textContent =
            "Security check failed. Close developer tools, disable extensions, " +
            "or try another browser. Error " + code + ".";
          return true;
        }

        return false; // let Turnstile log everything else
      }
    });
  }
</script>

<script
  src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=onTurnstileLoad"
  defer
></script>

Returning a non-falsy value from error-callback tells Turnstile you handled the error and suppresses its own console warning. Returning a falsy value (including undefined) makes Turnstile log the code for you. Cloudflare warns that with no error-callback at all, the widget throws a JavaScript exception on error.

Then gather identifiers before you ask anyone for help:

  • Run Cloudflare's compatibility troubleshooter at https://browser-compat.turnstile.workers.dev/ **in the same browser or app** that fails. It runs automation, clock and connectivity checks and emits a session ID plus a per-event log of the widget lifecycle.
  • Get the Ray ID from the end of the challenge page.
  • Click the Turnstile widget's success, failure or spinner logo four times to reveal the QR code for that specific challenge instance.
  • Submit a feedback report through the widget itself. The options are: the widget always fails, sometimes fails, is too slow, keeps looping, or other.

Fixes if it is your site

Rank these by how much traffic is affected, which you can see in Turnstile Analytics — it breaks down solve rate by hostname, browser, country, user agent, ASN, operating system and source IP. A 600010 concentrated in one browser or one ASN is a very different problem from one spread evenly.

  • **Handle the failure instead of looping.** A visitor stuck behind an invisible widget retrying every 8 seconds gets no feedback at all. Bound the retries and surface the code and Ray ID so they can report it.
  • **Load the script early.** Cloudflare recommends executing the Turnstile script "as early upon the visitor's page entry as possible", so verification is finished before the visitor tries to submit.
  • **Watch widget lifecycle in SPAs.** One report describes an invisible widget rendered on form submit and removed on navigation, which then failed for 20 or more consecutive retries once the flow was repeated. If you mount and unmount widgets, use turnstile.remove() for the old widget ID rather than tearing out the container.
  • **Confirm the hostname is canonical.** Trailing dots, and any host serving the page that is not in Hostname Management, are worth ruling out — though an unlisted domain normally reports 110200, not 600010.
  • **Do not build fallback logic that skips verification.** One reporter worked around 600010 by disabling strict token validation in their Worker and leaning on a honeypot and IP rate limiting. That is a spam hole, not a fix.

600010 versus the codes with a defined meaning

If your console shows one of these instead, you have a much more specific problem — and, unlike 600010, an actionable one.

CodeMeaningRetry
110100 / 400020Invalid sitekeyNo
110110Sitekey not foundNo
110200Domain not authorizedNo
400070Sitekey disabledNo
110600Challenge timed outYes
110620Interaction timed outYes
200100Clock or cache problemNo
200500Iframe load error — check whether challenges.cloudflare.com is blockedYes
300*Generic challenge failure — bot behavior detectedYes
600*Generic challenge failure — bot behavior detectedYes

300* and 600* carry identical descriptions and identical guidance in Cloudflare's table. Cloudflare has not published what distinguishes the two families, so do not infer anything from getting one rather than the other.

Common questions

It is a generic challenge failure. Cloudflare's error code reference documents the whole 600* family as "Generic challenge failure" with the note "Bot behavior detected", and marks it retryable. The specific 010 suffix is not documented — Cloudflare states that digits after a * "can vary and are for internal use" — so 600010 tells you the challenge failed without telling you why.

Almost certainly not, despite a widely-copied Cloudflare Community answer saying so. Sitekey problems have their own codes (110100, 110110, 400020, 400070), an unauthorised domain reports 110200, and the secret key is only ever used server-side against the siteverify API, so it cannot generate a client-side widget error. Verify your keys because it takes a minute, but expect the cause to be elsewhere.

Cloudflare's supported-browsers documentation states that developer tools "may modify browser behavior" and that device emulation "can trigger bot detection mechanisms", both of which land in the 600 family. Several reporters see 600010 with devtools open and a clean pass as soon as they close them. Test the real flow with devtools closed, and use a real device rather than emulation.

Yes, and it is one of the few causes with a clean reproduction. One report traced 600010 to an extension whose content script left the challenge-platform request at status 0 — never reaching the network — with the error disappearing the moment the extension was disabled for that domain. Ad blockers, script blockers, canvas and fingerprinting blockers, and VPN or proxy extensions are all documented by Cloudflare as sources of interference.

Cloudflare marks 600* as retryable and Turnstile retries automatically by default every 8000ms. That default hides individual failures and can leave a visitor in a silent loop. Set retry: "never" while debugging, implement bounded retries with turnstile.reset() inside error-callback, and show the visitor the error code once you give up.

Run Cloudflare's troubleshooter at browser-compat.turnstile.workers.dev in the same browser that fails and keep the session ID. Take the Ray ID from the end of the challenge page, and click the widget's success, failure or spinner logo four times to reveal the QR code for that challenge instance. Then submit a feedback report through the widget's Submit Feedback option with those identifiers and steps to reproduce.

Related

More in Turnstile errors

Automating a gate you own or are authorised to test?

// SolveGate clears Cloudflare Turnstile and WAF challenges through one REST call · first 1,000 solves free