How to Add reCAPTCHA to a Contact Form (v2 and v3)
Add Google reCAPTCHA v2 or v3 to an HTML or React contact form, verify every token without running your own server, and choose the version that suits your visitors.
Nisuform team
6 min read

Short answer: create a reCAPTCHA key in the Google Cloud console, load https://www.google.com/recaptcha/api.js and put <div class="g-recaptcha" data-sitekey="..."> inside your form for the v2 checkbox. Then make sure something holding your secret key checks each token with Google's siteverify API, because the checkbox alone blocks nothing. On a site without a server, post the form to a form backend that does the check. Nisuform verifies v2 and v3 tokens on every plan.
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<form action="https://api.nisuform.com/s/YOUR_FORM_KEY" method="POST">
<label for="email">Email</label>
<input id="email" type="email" name="email" required>
<label for="message">Message</label>
<textarea id="message" name="message" required></textarea>
<input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
<div class="g-recaptcha" data-sitekey="YOUR_RECAPTCHA_SITE_KEY"></div>
<button type="submit">Send</button>
</form>v2 checkbox or v3 score
reCAPTCHA comes in two versions that behave very differently on a form:
| v2 checkbox | v3 score | |
|---|---|---|
| What visitors see | "I'm not a robot", sometimes followed by an image puzzle | Nothing, apart from a badge in the corner |
| What Google returns | Pass or fail | A score from 0.0 to 1.0 |
| A real person who looks suspicious | Solves a puzzle and sends the message | Gets blocked, unless you build a fallback |
| Code | A <div> and a script tag | JavaScript that asks for a token on submit |
For a contact form, the checkbox is the safer choice. With v3, a real person who gets a low score is rejected and may not try again. v3 suits forms where you'd rather let a few bots through than show anyone a checkbox, and where you watch the rejected count.
What changed with reCAPTCHA keys
Google has moved reCAPTCHA into Google Cloud. Keys now live in Google Cloud projects, and keys from the old admin console that nobody moved by hand are moved automatically. Their site keys, secret keys and the siteverify endpoint keep working as before.
Two things are different for a new form:
- Usage is metered. The free tier is 10,000 checks a month, counted across your whole Google Cloud organization. Above that, Google asks you to turn on billing for the project.
- The secret key is hidden. The console shows the site key as the key's Key ID. The secret is behind Use legacy key on the key's Integration tab, or Reveal when you edit the key.
The checkbox doesn't stop bots on its own
When someone ticks the box, the widget adds a token to the form in a hidden field named g-recaptcha-response. A bot can skip your page and post to your form's endpoint directly, with no token at all. The token only matters if the server that receives the form sends it to https://www.google.com/recaptcha/api/siteverify with your secret key and rejects the submission when Google says no.
Each token expires after two minutes and can be verified once. For v3, the response also carries the score, and picking the cut-off is up to you. Google suggests starting at 0.5.
Your secret key can't go in HTML or browser JavaScript, so a static site has no place to make that call. Either run a small server function that verifies the token and then sends the email, or post the form to a form backend that holds the secret and checks every token. Nisuform does that on every plan, and rejects v3 tokens that score below 0.5.
Add the reCAPTCHA checkbox to a form
Create a checkbox key
In the Google Cloud console, open Fraud Defense, then the Keys tab, and create a website key. Choose the checkbox type, add every domain the form runs on, and save. Copy the Key ID, which is your site key.
Copy the secret key
On the key's Integration tab, click Use legacy key, or Integrate with a third-party service or plugin, and copy the secret key.
Add both keys to your form
Create a form in Nisuform. In its Settings, Spam protection, turn on Require a CAPTCHA, choose Google reCAPTCHA, paste the site key and the secret key, and save.
Put the checkbox inside the form
Use the code at the top, with your endpoint from the Setup tab in
actionand your site key indata-sitekey. The<div>has to sit inside the<form>element so its hidden field is sent. If you use the styled embed from the Setup tab, copy it again and the checkbox is already in it.Send a test
Tick the box, wait two seconds and send. The message should reach your Nisuform inbox and your email.
A reCAPTCHA v3 contact form
A v3 key has no widget. Load the script with your site key in the URL, ask for a token when the visitor submits, and send it as g-recaptcha-response. This version posts with fetch and shows the result on the page:
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_RECAPTCHA_SITE_KEY"></script>
<form id="contact">
<label for="email">Email</label>
<input id="email" type="email" name="email" required>
<label for="message">Message</label>
<textarea id="message" name="message" required></textarea>
<button type="submit">Send</button>
<p id="status" role="status"></p>
</form>
<script>
const SITE_KEY = 'YOUR_RECAPTCHA_SITE_KEY'
const form = document.getElementById('contact')
const status = document.getElementById('status')
form.addEventListener('submit', (event) => {
event.preventDefault()
grecaptcha.ready(async () => {
const data = new FormData(form)
data.set('g-recaptcha-response', await grecaptcha.execute(SITE_KEY, { action: 'submit' }))
const response = await fetch('https://api.nisuform.com/s/YOUR_FORM_KEY', {
method: 'POST',
headers: { Accept: 'application/json' },
body: data,
})
const result = await response.json()
status.textContent = result.ok ? 'Thanks! We got your message.' : result.message
if (result.ok) form.reset()
})
})
</script>Ask for the token on submit, not when the page loads. Google's docs say so, and a token fetched on page load expires two minutes later. When a visitor scores below 0.5, the response is captcha_failed and the message above shows Nisuform's error text. If that happens to people you know are real, switch the form to a checkbox key.
The checkbox in React
Google's script renders every .g-recaptcha element once, when it loads. A React form that mounts later needs to render its checkbox itself:
useEffect(() => {
const el = widget.current
if (window.grecaptcha?.render) {
if (!el.childElementCount) window.grecaptcha.render(el, { sitekey: el.dataset.sitekey })
} else if (!document.querySelector('script[src="https://www.google.com/recaptcha/api.js"]')) {
const script = document.createElement('script')
script.src = 'https://www.google.com/recaptcha/api.js'
script.async = true
document.head.append(script)
}
}, [])Put <div ref={widget} className="g-recaptcha" data-sitekey="..." /> inside the form, post new FormData(event.currentTarget) so the token goes along, and call grecaptcha.reset() after a failed submit, because the old token is spent. With reCAPTCHA on, the Setup tab generates the full component for React, Next.js, Vue, Svelte and Astro, with your site key filled in.
Hiding the v3 badge
v3 puts a reCAPTCHA badge in the bottom corner of every page that loads the script. Google allows hiding it if the branding stays visible in the form itself. Hide the badge with CSS:
.grecaptcha-badge { visibility: hidden; }Then add this line near the submit button, with links to Google's Privacy Policy and Terms of Service: "This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply."
Testing reCAPTCHA
For the v2 checkbox, Google publishes a test key pair that always passes and shows a warning in the widget:
| Key | Value |
|---|---|
| Site key | 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI |
| Secret key | 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe |
Use them on a separate test form in Nisuform, never the live one. For v3 there are no test keys. Google recommends a separate key for your test environment and warns that scores there may be inaccurate, because v3 learns from real traffic.
When every submission fails
Nisuform answers 403 with captcha_failed when the token is missing, expired, already used, rejected by Google or, for v3, scored below 0.5. Nothing is stored. If it happens every time:
- "Invalid key type" in the widget. The key type doesn't match the code. A checkbox key needs the
g-recaptchadiv, and a score key needsgrecaptcha.execute. - "Invalid domain for site key". Add the page's domain to the key's domain list in Google Cloud.
- Wrong keys. The site key on the page and the keys in the form's settings have to come from the same reCAPTCHA key.
- Missing token. The checkbox is outside the
<form>, or yourfetchbuilds its own body and leavesg-recaptcha-responseout. - Expired token. The visitor ticked the box more than two minutes before sending. The widget asks them to tick it again.
If you don't need to keep reCAPTCHA, Cloudflare Turnstile works the same way in Nisuform, without a Google Cloud project or a monthly cap. A low-traffic contact form may need only the default checks every Nisuform form runs.
Frequently asked questions
Is reCAPTCHA still free?
Up to 10,000 checks a month. The free tier is counted across your whole Google Cloud organization, all sites and keys together, and Google asks you to turn on billing for the project above that.
Should a contact form use reCAPTCHA v2 or v3?
The v2 checkbox, for most contact forms. A visitor who looks suspicious gets an image puzzle and can still send the message. v3 shows nothing, but it only returns a score, so a real person with a low score is blocked with no way through unless you build a fallback.
Where do I find my reCAPTCHA secret key?
In the Google Cloud console, open Fraud Defense, then the Keys tab, and open your key. On its Integration tab, click Use legacy key, or Integrate with a third-party service or plugin, and copy the secret. You can also click Edit key on the key details page and reveal the secret key there. The Key ID is your site key.
Can I use reCAPTCHA on a static site?
The widget works anywhere, but every token has to be verified with your secret key through Google's siteverify API, and the secret can't go in browser code. On a static site, post the form to a form backend that does the check. Nisuform verifies reCAPTCHA v2 and v3 tokens on every plan.
What score threshold should I use for reCAPTCHA v3?
Google suggests starting at 0.5, on a scale from 0.0 (very likely a bot) to 1.0 (very likely a person). Nisuform rejects v3 tokens below 0.5.
Can I hide the reCAPTCHA badge?
Yes, if the reCAPTCHA branding stays visible in the form. Hide the badge with .grecaptcha-badge { visibility: hidden; } and add "This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply." near the submit button, with links to both.
Will my Contact Form 7 reCAPTCHA keys work?
Contact Form 7 uses reCAPTCHA v3, so its keys are score keys. They work with Nisuform through the v3 code in this guide. The checkbox and the styled embed need a separate checkbox key.

