Skip to content

fix(contact): stop Turnstile from blocking the contact form - #31

Merged
dyascj merged 1 commit into
mainfrom
fix/turnstile-not-blocking
Jul 31, 2026
Merged

fix(contact): stop Turnstile from blocking the contact form#31
dyascj merged 1 commit into
mainfrom
fix/turnstile-not-blocking

Conversation

@dyascj

@dyascj dyascj commented Jul 31, 2026

Copy link
Copy Markdown
Owner

The Turnstile widget was never rendering on the deployed site, so no token was produced, so every submission was refused with "Could not verify you are human". The contact form was effectively down. Already deployed to production ahead of this PR to restore it.

Two changes.

The widget now renders explicitly after mount against a node held by bind:this. Auto-render fires once when the script loads, before hydration has settled, and the node it renders into can be replaced out from under it. The network trace showed Turnstile loading and talking to Cloudflare the whole time, which is what pointed at the DOM rather than the config.

A missing token is no longer treated as a rejection. A missing token means the widget did not render or something blocked it, not that the sender is a bot, and failing closed there takes the form down for anyone running a script blocker. It is logged instead. A token that is present and invalid is still rejected, and the rate limiter applies either way.

That is a deliberate downgrade in strictness. Turnstile is worth having as defence in depth, but a contact form that exists so strangers can reach you should not have a CAPTCHA as a single point of failure. I got that tradeoff wrong the first time.

Worth saying plainly: I still cannot verify the widget renders for a human. Turnstile refuses to issue tokens to automated browsers, which is its entire purpose, so headless testing cannot confirm the fix. What is verified is that the form accepts submissions again, on production, which is the part that was actually broken.

18 unit tests, 28 e2e against workerd, typecheck and build clean.

Rejecting submissions with no Turnstile token took the form down for real
people. The widget was not rendering: auto-render fires once when the
script loads, before hydration has settled, and the node it renders into
can be replaced out from under it. So no token was ever produced and every
submission was refused.

Renders explicitly after mount instead, against a node held by bind:this.

Also stops treating a missing token as a rejection. A missing token means
the widget did not render or something blocked it, not that the sender is a
bot, and failing closed there breaks the form for anyone running a script
blocker. It is logged instead. A token that is present and invalid is still
rejected, and the rate limiter still applies either way.

Turnstile is defence in depth here, not a gate the contact form depends on.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
cjdyas-design 158e0f3 Commit Preview URL

Branch Preview URL
Jul 31 2026, 07:30 PM

@dyascj
dyascj merged commit 7f9cd08 into main Jul 31, 2026
3 checks passed
@dyascj
dyascj deleted the fix/turnstile-not-blocking branch July 31, 2026 20:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant