fix(contact): stop Turnstile from blocking the contact form - #31
Merged
Conversation
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.
Deploying with
|
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.