Bot protection using Cloudflare Turnstile with automatic fallback to Google reCAPTCHA. One component, multiple providers, seamless experience.
Turnstile primary with reCAPTCHA fallback
Component is globally available
Full type definitions included
Seamless provider switching
Get up and running in just a few steps
Create an .npmrc file in your project root:
# .npmrc - Configure GitHub Package Registry
@directksa:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKENnpm install @directksa/direct-captcha-guard# .env
TURNSTILE_SITE_KEY=your_turnstile_site_key
RECAPTCHA_V2_SITE_KEY=your_recaptcha_v2_site_key
RECAPTCHA_V3_SITE_KEY=your_recaptcha_v3_site_key// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@directksa/direct-captcha-guard'],
captchaGuard: {
provider: 'turnstile',
turnstile: {
siteKey: process.env.TURNSTILE_SITE_KEY
},
recaptcha: {
v2SiteKey: process.env.RECAPTCHA_V2_SITE_KEY,
v3SiteKey: process.env.RECAPTCHA_V3_SITE_KEY,
version: 'v2'
},
fallback: {
enabled: true,
to: 'recaptcha'
}
},
app: {
head: {
script: [
{ src: 'https://challenges.cloudflare.com/turnstile/v0/api.js', async: true }
]
}
}
})The <CaptchaGuard> component is now globally available!
See the component in action with copy-paste ready code
Integrate captcha protection into your forms. The submit button is disabled until the captcha is verified. Try it out below!
<template>
<form @submit.prevent="handleSubmit">
<input v-model="email" type="email" placeholder="Email" />
<input v-model="password" type="password" placeholder="Password" />
<CaptchaGuard @verified="onCaptchaVerify" />
<button type="submit" :disabled="!captchaToken">
Submit
</button>
</form>
</template>
<script setup lang="ts">
import type { CaptchaVerifiedPayload } from '@directksa/direct-captcha-guard'
const email = ref('')
const password = ref('')
const captchaToken = ref<string | null>(null)
const captchaMethod = ref<string | null>(null)
function onCaptchaVerify(payload: CaptchaVerifiedPayload | null) {
if (payload) {
captchaToken.value = payload.token
captchaMethod.value = payload.method
}
}
async function handleSubmit() {
if (!captchaToken.value) return
await $fetch('/api/submit', {
method: 'POST',
body: {
email: email.value,
password: password.value,
captchaToken: captchaToken.value,
captchaMethod: captchaMethod.value
}
})
}
</script>Simulate Cloudflare Turnstile being unavailable to test the automatic fallback to reCAPTCHA
To test the automatic fallback mechanism, you can block the Cloudflare Turnstile domain in your system's hosts file. This will simulate Turnstile being unavailable, triggering the fallback to Google reCAPTCHA.
# Open hosts file
sudo nano /etc/hosts
# Add this line at the end
127.0.0.1 challenges.cloudflare.com
# Save and exit (Ctrl+X, Y, Enter)sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder# Open PowerShell as Administrator
notepad C:\Windows\System32\drivers\etc\hosts
# Add this line at the end
127.0.0.1 challenges.cloudflare.com
# Save the fileipconfig /flushdns# Open hosts file
sudo nano /etc/hosts
# Add this line at the end
127.0.0.1 challenges.cloudflare.com
# Save and exit (Ctrl+X, Y, Enter)sudo systemd-resolve --flush-caches
# or
sudo resolvectl flush-cachesComplete reference of all available options with defaults
| Option | Type | Default | Description |
|---|---|---|---|
provider | 'turnstile' | 'recaptcha' | 'turnstile' | Primary captcha provider to use |
turnstile.siteKey | string | undefined | Cloudflare Turnstile site key |
recaptcha.v2SiteKey | string | undefined | Google reCAPTCHA v2 site key |
recaptcha.v3SiteKey | string | undefined | Google reCAPTCHA v3 site key |
recaptcha.version | 'v2' | 'v3' | 'v2' | Default reCAPTCHA version when fallback occurs |
fallback.enabled | boolean | true | Enable automatic fallback to secondary provider |
fallback.to | 'recaptcha' | 'recaptcha' | Provider to fallback to when primary fails |
scriptTimeout | number | 2000 | Timeout in ms for script loading before fallback |
| Prop | Type | Default | Description |
|---|---|---|---|
fallback | 'v2' | 'v3' | config value | Override the default reCAPTCHA version for this component instance |
| Event | Payload | Description |
|---|---|---|
@verified | CaptchaVerifiedPayload | null | Emitted when captcha verification completes. Receives { token, method } on success or null on failure |
interface CaptchaVerifiedPayload {
token: string // Verification token to send to backend
method: 'turnstile' | 'recaptcha-v2' | 'recaptcha-v3'
}