How to add Google ReCAPTCHA v3 to web form

12 July 2022

This article explains client and server side implementation of Google ReCAPTCHA v3 and best practices how to read and use validation results.

Prerequisites:

Within your admin section You'll have to create Google reCAPTCHA keys (site key and sercret key). Site key is necessary for client side implementation and secret key for server side validation. During creation process it's good practice to add your production domains and also localhost, so you'll be able to test on your dev/localhost environment.

After creation is done, you will have someting like this (not real data on screen):

SITE-KEY: 6LdqgOIgAAAAVD_0RPyK9Vtkqve4rpj5mR3Xg-ZQ

SECRET-KEY: 6LdqgOIgAAAACDKdvFp33snv6bHGuOAgZl0w4QQcd

Now, let's prepare our web form with necessary modifications.

HTML / Client side:

<!-- 1. Our form we need to protect against bots -->
<form action="/url-to-post" method="post" id="frm">
    <label for="email">Your email:</label>
    <input type="text" name="email" id="email" />
    <button type="submit">Submit</button>
    <input type="hidden" name="recaptcha_response" id="recaptchaResponse">
</form>


<!-- 2. Connect to Google API -->
<script src="https://www.google.com/recaptcha/api.js?render=SITE-KEY:"></script>


<!-- 3. Get token and populate our hidden filed with it, this token will be server side verified -->
<script>
    grecaptcha.ready(function () {
        grecaptcha.execute('SITE-KEY:', { action: 'contact' }).then(function (token) {
            var recaptchaResponse = document.getElementById('recaptchaResponse');
            recaptchaResponse.value = token;
        });
    });
</script>

Server side validation:

To perform server side validation, you'll need to pass your data within google's check URL: https://www.google.com/recaptcha/api/siteverify

This can be done with any server side technology (PHP, C#, nodejs ...). PHP example:

$post_data = http_build_query(
    array(
        'secret' => '6LdqgOIgAAAACDKdvFp33snv6bHGuOAgZl0w4QQcd',
        'response' => $_POST['recaptcha_response'],
        'remoteip' => $_SERVER['REMOTE_ADDR']
    )
);
$opts = array('http' =>
    array(
        'method'  => 'POST',
        'header'  => 'Content-type: application/x-www-form-urlencoded',
        'content' => $post_data
    )
);
$context  = stream_context_create($opts);
$response = file_get_contents('https://www.google.com/recaptcha/api/siteverify', false, $context);
$result = json_decode($response);
if (!$result->success) {
    throw new Exception('Gah! CAPTCHA verification failed', 1);
}

$result form example above is response JSON object with properties:

{
  "success": true|false,      // whether this request was a valid reCAPTCHA token for your site
  "score": number             // the score for this request (0.0 - 1.0)
  "action": string            // the action name for this request (important to verify)
  "challenge_ts": timestamp,  // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
  "hostname": string,         // the hostname of the site where the reCAPTCHA was solved
  "error-codes": [...]        // optional
}

As you can see, validation can be successful, but there is an extra property score that describes result quality (0.0 - 1.0). Lower number === more likely a bot.

Bottom line, to be sure you are not dealing with bot, you need to get response with success set to true and score as higher as possible (I would go with score >= 0.5)