Contact and enquiry forms attract spam bots within days of going live. Google reCAPTCHA separates people from bots, but it only protects your form if your server checks the answer before it sends any email. This guide shows how to add reCAPTCHA v2 Checkbox, v2 Invisible and v3 to a form, and how to verify it correctly in PHP and Node.js, including on Domain India shared hosting.
Create a key pair in the reCAPTCHA admin console, put the site key in your page and keep the secret key on your server. The browser sends a token with the form; your server posts that token and the secret to Google's siteverify endpoint, and processes the form only if the answer says success, the hostname is yours and, for v3, the score and action are what you expect. Start with v2 Checkbox, add a honeypot field and rate limiting, and never trust a check that happens only in the browser.
1. Which reCAPTCHA version to use
| Version | What the visitor sees | What you get back | Best for |
|---|---|---|---|
| v2 Checkbox | An "I'm not a robot" tick box, sometimes an image challenge | Pass or fail | A first implementation; the most predictable |
| v2 Invisible | Nothing, unless Google thinks the visitor is risky | Pass or fail | Forms where you want no extra click |
| v3 | Nothing, ever | A score from 0.0 (bot) to 1.0 (human) and an action name | Busy sites that want to decide their own thresholds |
v2 and v3 keys are different key types, so a v2 site key will not work with v3 code or the other way round. If you are unsure, start with v2 Checkbox: it either passes or fails, which is easy to reason about.
Google now manages reCAPTCHA keys inside Google Cloud projects, and usage above a monthly free allowance is billed. Check Google's current reCAPTCHA pricing page before you rely on it for a high-traffic site. Cloudflare Turnstile and hCaptcha are alternatives that work the same way: a widget in the page and a server-side verify call.
2. Register your site and get the keys
- Open the reCAPTCHA admin consolewith a Google account and create a new site.
- Choose the type:reCAPTCHA v2 (then "I'm not a robot" or Invisible) or reCAPTCHA v3.
- Add your domains,such as
yourdomain.com. Subdomains of a listed domain are covered. Addlocalhostonly to a separate test key. - Copy both keys.The site key goes in your HTML and is public. The secret key stays on your server only.
Store the secret key outside public_html: in a config file one level above the web root, or in an environment variable. Never put it in JavaScript or commit it to a public repository.
3. Add the widget to your form
v2 Checkbox
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<form method="POST" action="/contact.php">
<input type="text" name="name" required>
<input type="email" name="email" required>
<textarea name="message" required></textarea>
<div class="g-recaptcha" data-sitekey="YOUR_SITE_KEY"></div>
<button type="submit">Send</button>
</form>When the visitor ticks the box, Google adds a hidden field named g-recaptcha-response to the form. That is the token your server must verify.
v2 Invisible
Bind reCAPTCHA to the submit button. The challenge appears only when Google is unsure.
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<form id="contactForm" method="POST" action="/contact.php">
<!-- your fields -->
<button class="g-recaptcha" data-sitekey="YOUR_SITE_KEY"
data-callback="onSubmit" data-size="invisible">Send</button>
</form>
<script>
function onSubmit(token) {
document.getElementById('contactForm').submit();
}
</script>Invisible reCAPTCHA requires you to show the reCAPTCHA branding or the notice text Google specifies, so check Google's current display rules.
v3
v3 has no widget. Ask for a token when the form is submitted and send it in a hidden field.
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>
<form id="contactForm" method="POST" action="/contact.php">
<!-- your fields -->
<input type="hidden" name="recaptcha_token" id="recaptcha_token">
<button type="submit">Send</button>
</form>
<script>
document.getElementById('contactForm').addEventListener('submit', function (e) {
e.preventDefault();
const form = this;
grecaptcha.ready(function () {
grecaptcha.execute('YOUR_SITE_KEY', { action: 'contact' }).then(function (token) {
document.getElementById('recaptcha_token').value = token;
form.submit();
});
});
});
</script>Request the token at submit time, not on page load: a v3 token expires after two minutes and can be verified only once.
4. Verify the token on your server
This is the step that actually stops bots. A bot never runs your JavaScript; it posts straight to your form handler. So the handler must check the token with Google before it does anything else.
PHP
This version uses file_get_contents() over HTTPS, which works on both our cPanel and DirectAdmin servers. (curl_exec also works on cPanel, but is usually disabled on DirectAdmin.)
<?php
function verify_recaptcha(string $token, string $secret): array {
$context = stream_context_create(['http' => [
'method' => 'POST',
'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
'content' => http_build_query(['secret' => $secret, 'response' => $token]),
'timeout' => 5,
]]);
$raw = @file_get_contents('https://www.google.com/recaptcha/api/siteverify', false, $context);
$data = $raw ? json_decode($raw, true) : null;
return is_array($data) ? $data : ['success' => false, 'error-codes' => ['request-failed']];
}
$secret = require __DIR__ . '/../recaptcha-secret.php'; // returns the secret string, stored outside public_html
$token = $_POST['g-recaptcha-response'] ?? $_POST['recaptcha_token'] ?? '';
$result = $token !== '' ? verify_recaptcha($token, $secret) : ['success' => false];
$ok = ($result['success'] ?? false)
&& ($result['hostname'] ?? '') === 'yourdomain.com';
// v3 only: also check the score and the action name
if (isset($result['score'])) {
$ok = $ok && $result['score'] >= 0.5 && ($result['action'] ?? '') === 'contact';
}
if (!$ok) {
http_response_code(400);
exit('Verification failed. Please try again.');
}
// Validate the fields, then send the email.If the check fails, stop. Do not send the email "just in case", and do not tell the visitor more than "verification failed".
Node.js (Express)
Node.js 18 and later include fetch, so no extra package is needed.
app.post('/contact', express.urlencoded({ extended: false }), async (req, res) => {
const token = req.body['g-recaptcha-response'] || req.body.recaptcha_token || '';
const r = await fetch('https://www.google.com/recaptcha/api/siteverify', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ secret: process.env.RECAPTCHA_SECRET, response: token }),
});
const data = await r.json();
const ok = data.success && data.hostname === 'yourdomain.com' &&
(data.score === undefined || (data.score >= 0.5 && data.action === 'contact'));
if (!ok) return res.status(400).send('Verification failed');
// validate fields, then send the email
res.send('Thanks, we received your message.');
});5. Choosing a v3 threshold
A v3 score is a signal, not a verdict. 0.5 is a sensible starting threshold for a contact form. Log the scores you receive for a week or two, then adjust: raise it if spam still gets through, lower it if real visitors are blocked. For borderline scores you can add friction instead of rejecting, for example by showing a v2 Checkbox or holding the message for review.
Use a distinct action name for each form (contact, signup, login) and check it on the server, so a token taken from one page cannot be replayed on another.
6. Layers that work alongside reCAPTCHA
reCAPTCHA stops most automated spam, but not all of it, and it does nothing about people pasting spam by hand. Add these cheap layers:
7. Running this on Domain India hosting
PHP forms on shared hosting. Verification over HTTPS works as shown in section 4. To send the email itself on our cPanel servers, use PHP mail() with the -f envelope sender: SMTP libraries fail there because the socket functions they need are disabled. How to use PHPMailer for contact forms shows a working pattern, and PHP disabled functions on shared hosting explains the limits. On DirectAdmin, test mail sending on your plan.
Sending limits. Shared hosting allows 200 messages per hour per account on cPanel and 1,000 per day on DirectAdmin, so a form flooded by bots can use up your allowance. That is another reason to verify every token.
WordPress. Most form plugins have a reCAPTCHA or Turnstile setting where you paste the two keys, so you do not need to write code. Use the version the plugin asks for, because v2 and v3 keys are not interchangeable.
Node.js apps. The Express example runs on cPanel's Setup Node.js App tool or on the App Platform, where Node.js apps are detected automatically. Keep the secret in an environment variable.
- 25 GB NVMe SSD Storage
- 50 GB Monthly Bandwidth
- 1 Website
- 10 Email Accounts
The price on the card is a live Domain India list price and excludes 18% GST.
Frequently asked questions
Which reCAPTCHA version should I use for a contact form?
Start with reCAPTCHA v2 Checkbox. It shows an "I'm not a robot" box and returns a simple pass or fail, which is easy to verify. Move to v3 later if you want no visible widget and are ready to tune a score threshold.
Is it enough to add the reCAPTCHA widget to my form?
No. The widget only produces a token. Your server must send that token and your secret key to Google's siteverify endpoint and reject the submission unless the answer says success. Bots post directly to your form handler and never run the widget.
What score should I use for reCAPTCHA v3?
0.5 is a common starting point. Log the scores for a week or two, then raise the threshold if spam gets through or lower it if genuine visitors are blocked.
Why does reCAPTCHA say "invalid site key" or "invalid key type"?
The key does not match the code or the domain. v2 and v3 keys are different types, and the page's domain must be listed for the key in the reCAPTCHA admin console.
Why does verification fail with timeout-or-duplicate?
A token can be verified only once and expires after two minutes. Request the v3 token when the form is submitted, not on page load, and do not verify the same token twice.
Can I verify reCAPTCHA in PHP on Domain India shared hosting?
Yes. Posting to Google's siteverify endpoint with file_get_contents over HTTPS works on our cPanel and DirectAdmin servers. On cPanel, curl_exec also works; on DirectAdmin it is usually disabled.
Ready to protect your forms? Set up reCAPTCHA as above, send mail the way our PHPMailer guide describes, and compare cPanel hosting plans if you need a home for your site. If your form still sends spam or no mail at all, open a support ticket with the form's URL.
Send us the page address and what happens when you submit the form, and we will check the server side with you.
Open a support ticket