Turnstile verification loop: it completes, then starts over
A verification loop is the challenge that keeps coming back. The widget completes, the page reloads or the form submits, and Turnstile issues another challenge — sometimes forever. Cloudflare's own name for it is a challenge loop, and their documentation says it happens "in very specific cases where we detect strong bot signals". That is one of five documented causes, and the others are far more common in practice.
This is not the spinner that never finishes
Two symptoms get reported with the same words and have almost nothing in common:
| Symptom | What it is | Where to go |
|---|---|---|
| The widget never resolves — spinner, no token | The challenge did not complete | stuck on verifying you are human |
| The widget completes, then challenges again | The challenge completed and was re-issued | This page |
| The widget mounts and then stops responding | A stalled widget, reported as 300030 | error 300030 |
The distinction matters because the fixes do not overlap. A challenge that never completes is a client-side problem — a blocked script, an unsupported browser, a container with no height. A challenge that completes and repeats is about what happened to the token afterwards, or about signals that made Cloudflare ask again.
Cloudflare's five documented causes
From the challenge solve issues page, verbatim, a challenge loop can happen because of:
- Network issues. Poor or unstable connections prevent the challenge from being completed.
- Browser configuration. Settings or extensions may block the scripts needed to execute the challenge.
- Unsupported browsers. A browser Turnstile does not support.
- JavaScript disabled. Turnstile relies on JavaScript to function.
- Detection errors. If Turnstile suspects bot-like behaviour, repeated challenges are issued.
Cloudflare adds that most challenges complete in a few seconds, and that a longer one is a sign to check the network. Their troubleshooting sequence is browser compatibility, then extensions, then JavaScript, then private mode, then another device, then no VPN, then another network.
That list is written for a visitor stuck on someone else's site. If you own the site, the useful version is different — most loops reported by developers are not detection at all.
The causes that are yours to fix
The token was never validated. A token proves nothing until your server hands it to siteverify. If your handler skips validation on some path — an early return, a cached response, a route that bypasses middleware — the gate in front of it re-challenges because nothing ever cleared it.
The token was validated twice. A Turnstile token is single-use and valid for 300 seconds. The second validation of the same token returns success: false with timeout-or-duplicate, and code that treats any failure as "challenge again" produces a loop by itself. Retry middleware, double-submitted forms and a validation call in both a middleware and a controller all cause this. See timeout-or-duplicate.
The widget was reset without being re-rendered. After a failed submit, calling turnstile.reset() returns the widget to its unsolved state. If your code resets on every submit — including the successful ones — the visitor solves, submits, and is handed a fresh challenge as a reward.
Pre-clearance is expected but not configured. In pre-clearance mode Turnstile sets a cf_clearance cookie so a solved challenge satisfies the WAF for later requests. That cookie is set by a fetch to a /cdn-cgi/ endpoint on your own domain, and Cloudflare documents that the request needs 'self' in your connect-src. A CSP that omits it means the challenge succeeds, no clearance cookie is written, and the next protected request challenges again — which is exactly what a loop looks like. See pre-clearance and cf_clearance.
The cookie cannot survive. A clearance cookie set on www.example.com does nothing for example.com. Cookie blocking, private windows, SameSite interactions on a cross-site POST and a mismatched host all produce a loop that looks like detection and is not.
Narrowing it down
Work from the token outwards. Each step rules out a whole class:
# 1. Did a token arrive at all? Read the hidden field after solving. # Empty -> not a loop; the challenge is not completing. Go to # /turnstile-errors/stuck-verifying-you-are-human instead. # 2. Did your server validate it, and what did Cloudflare answer? curl -s https://challenges.cloudflare.com/turnstile/v0/siteverify \ -d secret="$TURNSTILE_SECRET" \ -d response="$TOKEN" | jq # {"success": true} -> the token was fine; the loop is # downstream (cookie, redirect, reset) # "timeout-or-duplicate" -> validated twice, or older than 300s # "invalid-input-response" -> wrong secret for this sitekey # 3. Is a clearance cookie being set? Pre-clearance only. curl -sI https://example.com/protected | grep -i set-cookie | grep cf_clearance
If step 2 says success: true and the loop continues, nothing is wrong with Turnstile: the token was good and something after it did not record that fact. That is a session, cookie or redirect bug, and it is where to spend the next hour.
If you reach the end of that and the widget is genuinely being re-challenged on a clean browser with a valid token, you are in the detection case Cloudflare describes — and there, the answer is a feedback report through the widget's Submit Feedback control rather than a code change.
Loops in automation, which are a different problem
A loop in a test suite or a scripted client usually is detection, because the client genuinely is automated. Cloudflare's own testing guidance says automated suites driven by Selenium, Cypress or Playwright are detected as bots, and recommends the documented dummy sitekeys precisely so that tests stop depending on passing a real challenge. For anything that is really testing your own form logic, the test keys are the right answer and they are deterministic.
When the challenge itself is what has to be cleared — a staging environment behind a real widget, synthetic monitoring against production, a property you are authorised to test — a solver returns a real token for the sitekey and page URL, and the mode makes no difference to the request. Prepaid credits start at $0.40 per 1,000 solves and fall to $0.075 at volume; failed solves are never billed; the first 1,000 are free. The solve reference has the call.
Common questions
Most often because nothing recorded that you solved it. A token proves nothing until your server validates it with siteverify, and in pre-clearance mode a clearance cookie has to be written and then survive. If either step is missing the next request looks unverified, which is indistinguishable from a loop.
No. A loop means the challenge completed and was issued again; a spinner means it never completed. They have separate causes and separate fixes — a loop is usually about the token or the cookie afterwards, a spinner about the script or the container.
Yes, and it is a common one. Tokens are single-use with a 300-second lifetime, so the second validation returns success false with timeout-or-duplicate. Code that re-challenges on any validation failure then loops indefinitely, and both calls looked correct in isolation.
Sometimes, but it is the last thing to conclude rather than the first. Cloudflare lists detection as one of five causes alongside network problems, browser extensions, unsupported browsers and disabled JavaScript — and on a site you control, an unvalidated token or a clearance cookie that cannot be set explains far more loops than detection does.
Related
Sources
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