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
});