Bugfixing comic illustration

Fix Turnstile 'Verification failed' in production


If your contact form works locally but fails in production with errors like:

  • Please complete the verification challenge
  • Verification failed. Please try again.

the issue is usually configuration, not form code.

Fast diagnosis checklist

1) Confirm site and secret keys are both set in production

You need:

  • PUBLIC_TURNSTILE_SITE_KEY (public var)
  • TURNSTILE_SECRET_KEY (secret)

If one is missing, token verification will fail.

2) Confirm Turnstile widget domain settings

In Cloudflare Turnstile, allowed domains should include:

  • your real domain (e.g. mayfield.io)
  • localhost for local testing

If production domain is missing, valid challenges may still fail verification.

3) Verify server sends token to Turnstile endpoint

Your backend should POST token + secret to:

https://challenges.cloudflare.com/turnstile/v0/siteverify

and check success === true before sending email.

4) Test API path directly

Without a valid token, this should fail (that is expected):

curl -i -X POST "https://yourdomain.com/api/contact" \
  -H "Content-Type: application/json" \
  -d '{"name":"Test","email":"test@example.com","message":"hello"}'

If this succeeds without token, server-side verification is not enforced correctly.

Common production mistakes

  • TURNSTILE_SECRET_KEY set locally but not on Worker/host
  • secret accidentally added as public var instead of secret
  • domain mismatch in Turnstile widget settings
  • stale deploy running old env values

Concrete fix flow

  1. set/update keys in production env
  2. redeploy
  3. submit form once from production URL
  4. confirm API returns success and message sends

If your app uses email provider fallback logic, ensure failure in Turnstile verification blocks send path completely.

Turnstile issues can feel random, but once env + domain + server verification are aligned, it is usually very stable.