Home

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#

Code#

Create a new function in your project:


_10
supabase functions new cloudflare-turnstile

And add the code to the index.ts file:

index.ts

_39
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts'
_39
import { corsHeaders } from '../_shared/cors.ts'
_39
_39
console.log('Hello from Cloudflare Trunstile!')
_39
_39
function ips(req: Request) {
_39
return req.headers.get('x-forwarded-for')?.split(/\s*,\s*/)
_39
}
_39
_39
serve(async (req) => {
_39
// This is needed if you're planning to invoke your function from a browser.
_39
if (req.method === 'OPTIONS') {
_39
return new Response('ok', { headers: corsHeaders })
_39
}
_39
_39
const { token } = await req.json()
_39
const clientIps = ips(req) || ['']
_39
const ip = clientIps[0]
_39
_39
// Validate the token by calling the
_39
// "/siteverify" API endpoint.
_39
let formData = new FormData()
_39
formData.append('secret', Deno.env.get('CLOUDFLARE_SECRET_KEY') ?? '')
_39
formData.append('response', token)
_39
formData.append('remoteip', ip)
_39
_39
const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'
_39
const result = await fetch(url, {
_39
body: formData,
_39
method: 'POST',
_39
})
_39
_39
const outcome = await result.json()
_39
console.log(outcome)
_39
if (outcome.success) {
_39
return new Response('success', { headers: corsHeaders })
_39
}
_39
return new Response('failure', { headers: corsHeaders })
_39
})

Deploy the server-side validation Edge Functions#


_10
supabase functions deploy cloudflare-turnstile
_10
supabase secrets set CLOUDFLARE_TURNSTILE_SECRET_KEY=your_secret_key

Invoke the function from your site#


_10
const { data, error } = await supabase.functions.invoke('cloudflare-turnstile', {
_10
body: { token },
_10
})