#captcha
5 messages · Page 1 of 1 (latest)
Hi @strange pilot. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
Convex’s docs and support threads don’t describe a full, step‑by‑step Turnstile (or other CAPTCHA) integration, but they do outline the recommended pattern conceptually:
-
Protect the “entry point” with CAPTCHA, not every Convex function.
Convex recommends treating CAPTCHA as a way to authorize an anonymous session ID, then rate‑limit and gate behavior based on that session, not on IP alone. [Rate limiting] -
Use Next.js as the CAPTCHA boundary, Convex as the app backend.
From the Discord thread, people wanting Turnstile + Convex are doing exactly what you’re suggesting: wrap Convex calls behind a Next.js route or server action that:- Reads the client IP (from
X-Forwarded-Fororreq.ipdepending on your hosting), - Verifies the CAPTCHA with Cloudflare,
- Only then calls Convex. [Discord IP thread]
- Reads the client IP (from
-
Why not do CAPTCHA directly in Convex?
- Convex functions (queries/mutations/actions) don’t currently expose the client IP in
ctx, which Turnstile often wants. [Discord IP thread] - You can get
X-Forwarded-Forin HTTP Actions, but that still leaves you implementing the Turnstile verification logic yourself and then calling into your internal Convex functions. [Discord IP thread] - Because of this, the most straightforward pattern with Next.js is to use Next.js route handlers / server actions as the CAPTCHA verification layer.
- Convex functions (queries/mutations/actions) don’t currently expose the client IP in
High‑level implementation overview (Next.js App Router + Convex)
Client side (Next.js page / component)
- Render the Turnstile widget (or other CAPTCHA) in your form.
- On submit:
- Get the CAPTCHA token from the widget.
- POST to a Next.js route handler, e.g.
POST /api/submitForm, with:captchaToken- Your form data.
Next.js route handler (app/api/submitForm/route.ts)
- Extract:
captchaTokenand form data from the request body.- Client IP from headers (e.g.
x-forwarded-for) if Turnstile requires it.
- Call Cloudflare Turnstile’s verify endpoint with:
- Your secret key,
- The
captchaToken, - The client IP (if required).
- If verification fails → return 4xx.
- If verification succeeds:
- Call Convex from the server using
fetchMutation/fetchActionfromconvex/nextjsto perform the actual work. [Next.js module] - Optionally associate a session ID (cookie or header) with “captcha passed” and use that to gate future calls, as suggested in the rate‑limiting article. [Rate limiting]
- Call Convex from the server using
Convex side
- Your Convex mutation/action just assumes “this request has already passed CAPTCHA” because the Next.js route handler enforces it.
- For anonymous users, you can:
- Pass a session ID in the args and rate‑limit by that ID, as recommended in the rate‑limiting guide. [Rate limiting]
Using Convex HTTP Actions instead of Next.js routes
If you prefer to stay inside Convex:
- Create a Convex HTTP Action that:
- Reads
X-Forwarded-Forto get the client IP. [Discord IP thread] - Verifies the Turnstile token with Cloudflare.
- On success, calls internal Convex mutations/actions.
- Reads
- Call that HTTP Action from your Next.js client instead of calling Convex directly.
Summary
- Convex doesn’t have a built‑in Turnstile integration; the preferred pattern is:
- CAPTCHA in the frontend,
- Verification in a trusted server boundary (Next.js route handler or Convex HTTP Action),
- Then call Convex only after verification.
- For anonymous users, combine this with a session ID–based rate‑limiting strategy rather than relying on IP alone. [Rate limiting]