Switch from Turnstile
Migrate from Cloudflare Turnstile to gkCAPTCHA in under 15 minutes.
Why Switch?
- PDPL compliance: gkCAPTCHA is fully compliant with Saudi Arabia's Personal Data Protection Law. Cloudflare Turnstile processes data on Cloudflare's global network, which may route through servers outside Saudi Arabia.
- Data residency: All data is processed and stored in our Riyadh data center. No user data ever leaves Saudi Arabia.
- Privacy-first: gkCAPTCHA uses no tracking cookies, no persistent fingerprinting, and no cross-site tracking. Behavioral data is processed in real-time and never stored.
Code Changes
Client-Side (HTML)
Before (Turnstile)
index.html
<!-- Cloudflare Turnstile -->
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<form action="/submit" method="POST">
<div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div>
<button type="submit">Submit</button>
</form>After (gkCAPTCHA)
index.html
<!-- gkCAPTCHA -->
<script src="https://gkcaptcha.gatekeeper.sa/widget/gk-captcha.js" async defer></script>
<form action="/submit" method="POST">
<gk-captcha site-key="pk_live_..."></gk-captcha>
<button type="submit">Submit</button>
</form>Server-Side (Verification)
Before (Turnstile)
server.ts
// Turnstile server-side verification
const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
secret: process.env.TURNSTILE_SECRET_KEY,
response: req.body['cf-turnstile-response'],
}),
})
const data = await response.json()
// { success: bool, challenge_ts, hostname, "error-codes": [], action, cdata }After (gkCAPTCHA)
server.ts
// gkCAPTCHA server-side verification
const response = await fetch('https://gkcaptcha.gatekeeper.sa/api/v1/token/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.GKCAPTCHA_SECRET_KEY}`,
},
body: JSON.stringify({ token: req.body['gk-captcha-token'] }),
})
const data = await response.json()
// { success: bool, score: 0.0-1.0, timestamp, reason_code? }Step-by-Step Migration
- Sign up at gatekeeper.sa and create a site to get your site key (pk_live_...) and secret key (sk_live_...).
- Remove the Turnstile script tag: remove the script loading challenges.cloudflare.com/turnstile/v0/api.js.
- Add the gkCAPTCHA script tag: <script src="https://gkcaptcha.gatekeeper.sa/widget/gk-captcha.js" async defer></script>
- Replace the widget element: change <div class="cf-turnstile" ...> to <gk-captcha site-key="pk_live_...">.
- Update server-side verification: change the endpoint from challenges.cloudflare.com/turnstile/v0/siteverify to gkcaptcha.gatekeeper.sa/api/token/verify. Use JSON body with Authorization header instead of form-encoded params.
- Update environment variables: rename TURNSTILE_SECRET_KEY to GKCAPTCHA_SECRET_KEY and TURNSTILE_SITE_KEY to GKCAPTCHA_SITE_KEY.
- Test in staging: verify the widget renders, verification succeeds, and your form submission flow works end-to-end.
Testing Checklist
- Widget renders on the page
- Verification succeeds with valid interaction
- Verification rejects without interaction
- Error handling works when API is unreachable
- Form submission flow works end-to-end
- Mobile layout renders correctly