Best Practices
Follow these guidelines to ensure secure and optimal integration of gkCAPTCHA.
Security
Keep Your Secret Key Secure
- Never expose your secret key in client-side code
- Store it in environment variables
- Rotate keys if they are compromised
javascript
// Server-side only
const secretKey = process.env.GKCAPTCHA_SECRET_KEY;
// NEVER do this
const secretKey = "sk_live_xxxx"; // Exposed in client bundle!Always Verify Server-Side
Never trust client-side verification alone. Always verify the token on your server.
javascript
// WRONG: Trusting client-side result
if (captchaVerified) {
processForm();
}
// CORRECT: Verify on server
const serverResult = await verifyCaptcha(token);
if (serverResult.verified) {
processForm();
}Check the Hostname
Verify that the hostname in the response matches your expected domain.
javascript
const result = await verifyCaptcha(token);
if (result.verified && result.hostname === 'yourdomain.com') {
// Safe to proceed
}Performance
Load the Widget Asynchronously
Use async and defer to prevent blocking page load.
html
<script src="https://gkcaptcha.gatekeeper.sa/widget/gk-captcha.js" async defer></script>Lazy Load When Possible
Only load the widget when the user reaches the form.
javascript
// Load widget when form is in viewport
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
loadCaptchaWidget();
observer.disconnect();
}
});
observer.observe(document.getElementById('contact-form'));Use Invisible Mode for Low-Friction UX
For trusted users or low-risk actions, use invisible mode.
javascript
gkCaptcha.render('container', {
siteKey: 'your-site-key',
mode: 'invisible'
});User Experience
Provide Clear Feedback
Show loading states and error messages.
javascript
gkCaptcha.render('container', {
siteKey: 'your-site-key',
onSuccess: (token) => {
showSuccessMessage('Verification complete');
enableSubmitButton();
},
onError: (error) => {
showErrorMessage('Verification failed. Please try again.');
resetCaptcha();
}
});Handle Token Expiration
Tokens expire after 5 minutes. Handle expiration gracefully.
javascript
gkCaptcha.render('container', {
siteKey: 'your-site-key',
onExpired: () => {
showMessage('Verification expired. Please verify again.');
disableSubmitButton();
}
});Common Mistakes
Mistake 1: Not Handling Errors
javascript
// BAD
try {
await verifyCaptcha(token);
processForm();
} catch (e) {
// Silent failure!
}
// GOOD
try {
const result = await verifyCaptcha(token);
if (result.verified) {
processForm();
} else {
showError('Please complete verification');
}
} catch (e) {
logger.error('CAPTCHA verification error', e);
showError('Verification service unavailable');
}Mistake 2: Caching Tokens
javascript
// BAD - Token reuse vulnerability
const cachedToken = localStorage.getItem('captcha-token');
if (cachedToken) {
submitForm(cachedToken);
}
// GOOD - Always get fresh token
widget.execute().then(token => {
submitForm(token);
});Mistake 3: Client-Only Validation
javascript
// BAD - Client-side only
document.querySelector('form').addEventListener('submit', (e) => {
if (captchaWidget.isVerified()) {
// Submit form - but server doesn't verify!
}
});
// GOOD - Server verification
document.querySelector('form').addEventListener('submit', async (e) => {
e.preventDefault();
const token = captchaWidget.getToken();
const response = await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify({
...formData,
captchaToken: token
})
});
// Server verifies token before processing
});