Skip to content

feat(website): protect contact form with Cloudflare Turnstile - #58

Merged
gabrielrangel95 merged 1 commit into
mainfrom
feat/contact-turnstile
Oct 13, 2026
Merged

gabrielrangel95 merged 1 commit into
mainfrom
feat/contact-turnstile

Conversation

@gabrielrangel95

Copy link
Copy Markdown
Contributor

Summary

The contact form has been receiving a lot of bot submissions. This adds Cloudflare Turnstile:

  • ContactPage.tsx loads the Turnstile script, renders the widget (dark theme), sends the token with the form, and resets the widget after a failed submit (tokens are single-use).
  • /api/contact verifies the token via Cloudflare siteverify (with the client IP) before calling Resend, and returns 403 on a missing, invalid, or reused token.
  • The domain does not need to be on Cloudflare; only a free Cloudflare account with a Turnstile widget for concepta.dev.

Required env vars (Vercel: Production + Preview)

  • NEXT_PUBLIC_TURNSTILE_SITE_KEY
  • TURNSTILE_SECRET_KEY

⚠️ In production, a missing TURNSTILE_SECRET_KEY fails closed: every submission is rejected. Add the preview hostnames (e.g. *.vercel.app) to the widget in Cloudflare too.

Test plan

  • pnpm test:site (47 passing, including a new Turnstile test)
  • Locally with real keys: valid token → 200, reused token → 403, no token → 403
  • Cloudflare test secrets: always-pass → 200, always-fail → 403

🤖 Generated with Claude Code

The contact form was getting a lot of bot submissions that the honeypot
didn't catch. The form now renders a Turnstile widget and sends its token
with the submission; /api/contact verifies it with Cloudflare's siteverify
endpoint (passing the client IP) before sending anything through Resend,
and returns 403 when the token is missing, invalid, or reused. The widget
is reset after a failed submit since tokens are single-use.

Requires NEXT_PUBLIC_TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY. Without
the secret, verification is skipped in development and fails closed in
production.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
mix-docs Ready Ready Preview Oct 13, 2026 1:49pm UTC

@gabrielrangel95
gabrielrangel95 merged commit 51453d2 into main Oct 13, 2026
7 checks passed
@gabrielrangel95
gabrielrangel95 deleted the feat/contact-turnstile branch October 13, 2026 13:49

This branch was successfully deployed

1 active deployment
Preview – mix-docs — efdd95dc Deployed Oct 13, 2026 by vercel[bot]
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