CAPTCHA support with Cloudflare Turnstile
Cloudflare Turnstile is a friendly, free CAPTCHA replacement, and it works seamlessly with Supabase Edge Functions to protect your forms. View on GitHub.
Setup
- Follow these steps to set up a new site: https://developers.cloudflare.com/turnstile/get-started/
- Add the Cloudflare Turnstile widget to your site: https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/
Code
Create a new function in your project:
1supabase functions new cloudflare-turnstileAnd add the code to the index.ts file:
1import { corsHeaders } from '../_shared/cors.ts'23console.log('Hello from Cloudflare Trunstile!')45function ips(req: Request) {6 return req.headers.get('x-forwarded-for')?.split(/\s*,\s*/)7}89Deno.serve(async (req) => {10 // This is needed if you're planning to invoke your function from a browser.11 if (req.method === 'OPTIONS') {12 return new Response('ok', { headers: corsHeaders })13 }1415 const { token } = await req.json()16 const clientIps = ips(req) || ['']17 const ip = clientIps[0]1819 // Validate the token by calling the20 // "/siteverify" API endpoint.21 let formData = new FormData()22 formData.append('secret', Deno.env.get('CLOUDFLARE_SECRET_KEY') ?? '')23 formData.append('response', token)24 formData.append('remoteip', ip)2526 const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'27 const result = await fetch(url, {28 body: formData,29 method: 'POST',30 })3132 const outcome = await result.json()33 console.log(outcome)34 if (outcome.success) {35 return new Response('success', { headers: corsHeaders })36 }37 return new Response('failure', { headers: corsHeaders })38})Deploy the server-side validation Edge Functions
1supabase functions deploy cloudflare-turnstile2supabase secrets set CLOUDFLARE_SECRET_KEY=your_secret_keyInvoke the function from your site
1const { data, error } = await supabase.functions.invoke('cloudflare-turnstile', {2 body: { token },3})