DEV Community

Cover image for Implementing reCAPTCHA V3
The Dev Drawer
The Dev Drawer

Posted on

Implementing reCAPTCHA V3

If you are being bothered by spam and bot submissions on your website or a client website, reCAPTCHA is your answer. reCAPTCHA is powered by Google and stands up to most bot spam. You may have used it before or you may be interested in using it now, but with V3 you may not know where to start.

In this tutorial, we will implement reCAPTCHA V3 from start to finish and use Vanilla JS to validate and submit our form using fetch.

View This On YouTube

NOTE: This tutorial uses WAMP as the server since we use PHP to process the sending of the email and validation of reCaptcha. You can use your own server setup. I used WAMP since it is easiest for most users to get started with.

Folder Structure

We are using a basic HTML structure with a single PHP file for processing the reCaptcha validation and sending the email. You can set up your files however you like but for the sake of this tutorial, you can replicate what I have below.

index.html
thanks.html
send.php
/assets
   /js
      init.js
   /sass
      style.scss
/css (generated by Sass)
   style.css
   style.min.css
Enter fullscreen mode Exit fullscreen mode

Creating Our Form

We are using a basic HTML form without any processors. I have added Bootstrap for visual appearance but it is not required for this tutorial. I also included a CSS reference to the style.min.css file that is created by Sass.

In the form HTML, you will see the form elements and their error messages using the class invalid-feedback. Make sure you have the error message div as our JS relies on it. Also, make sure you have the entire form wrapped in a formfields div as I do below.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="/css/style.min.css" rel="stylesheet">
    <title>reCAPTCHA V3 Demo</title>
</head>
<body>
    <div class="container">
        <br>
        <h1 class="text-center">reCAPTCHA V3 Demo</h1>
        <br>
        <form method="post" id="contact" class="contact">
            <input type="hidden" name="recaptchaResponse" id="recaptchaResponse">
            <div class="formfields">
                <div class="row">
                    <div class="col-md-6">
                        <div class="form-element">
                            <input type="text" name="fname" id="fname" class="form-control form-control-lg" placeholder="First Name *">
                            <div class="invalid-feedback">
                                Please enter your first name.
                            </div>
                        </div>
                        <br>
                    </div>
                    <div class="col-md-6">
                        <div class="form-element">
                            <input type="text" name="lname" id="lname" class="form-control form-control-lg" placeholder="Last Name *">
                            <div class="invalid-feedback">
                                Please enter your last name.
                            </div>
                        </div>
                        <br>
                    </div>
                </div>
                <div class="form-element">
                    <input type="text" name="email" id="email" class="form-control form-control-lg" placeholder="Email Address *">
                    <div class="invalid-feedback">
                        Please enter your email address.
                    </div>
                </div>
                <br>
                <div class="form-element">
                    <textarea name="message" id="message" cols="30" rows="10" class="form-control" placeholder="How can I help you? *"></textarea>
                    <div class="invalid-feedback">
                        Please enter a message.
                    </div>
                </div>
                <br>
                <button type="submit" class="btn btn-lg btn-primary" id="submit-button">Send Message</button>
                <br>
                <br>
                <p class="text-center">
                    <small>This site is protected by reCAPTCHA and the Google <a href="https://policies.google.com/privacy">Privacy Policy</a> and <a href="https://policies.google.com/terms">Terms of Service</a> apply.</small>
                </p>
            </div>
            <div id="alert" class="text-center"></div>
        </form>
    </div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

That is it for the HTML for now. If you view this page, you should see a standard form.

JS Validation

In this section, we will handle the JS validation and display of our error or success messages. First, we load JS then we grab the form. Once the form is submitted we loop through the fields and validate them one by one. If they do not contain any data, we simply display an error message and highlight the field. If they are good, we highlight the field with a green border.

Once all fields are validated and are correct, we hide the form and display a waiting message.

window.addEventListener("load", function(){ // Wait for the page to load

    "use strict"; // Strict mode for JavaScript

    const form = document.querySelector(".contact") // Get the form

    form.addEventListener("submit", function (event){
        event.preventDefault() // Prevent the default action of the form
        let fields = document.querySelectorAll(".contact .form-control") // Get all the fields
        let valid = true
        for (var i = 0; i < fields.length; i++) {
            fields[i].classList.remove("no-error") // Remove the no-error class from all fields
            if(fields[i].value === ""){ // If the field is empty
                fields[i].classList.add("has-error")
                fields[i].nextElementSibling.style.display = "block"
                valid = false
            }else{ // If the field is not empty
                fields[i].classList.remove("has-error")
                fields[i].classList.add("no-error")
                fields[i].nextElementSibling.style.display = "none"
            }
        }
        if(valid){ // If all the fields are valid
            document.querySelector(".formfields").style.display = "none"
            document.querySelector("#alert").innerText = "Processing your submission, please wait..."
        }
    })
})
Enter fullscreen mode Exit fullscreen mode

Just after your HTML body, add the following code to reference your JS file:

<script src="/assets/js/init.js"></script>
Enter fullscreen mode Exit fullscreen mode

If you made it this far, then you should have a form that validates your inputs but does not show in color. We do that next with the CSS.

Sass

The CSS is how I like my fields to look so you can change it to however you want it to look or you can keep it like mine.

NOTE: Keep in mind I am using Sass to compile my CSS so you will need a processor like Live Sass Compiler for VS Code. I have it set up so it processes my Sass, it adds a style.css and style.min.css to my CSS folder.

body {
    color:#333;
}

.form-control {
    border-radius: 0px;
    border: 1px solid #333;
    &.form-control-lg {
        height:calc(3.5rem + 2px);
        padding:1rem 0.75rem;
        font-size:1rem;
    }
    &::placeholder {
        font-size:1rem
    }
    &.has-error {
        border:1px solid red;
    }
    &.no-error {
        border:1px solid green;
    }
}

.error {
    color:red;
}

.success {
    color: green
}

.btn {
    border-radius: 0;
    border:0;
    &.btn-primary {
        background-color:#38a4ef;
        &:hover {
            background-color: #2786c9;
        }

Enter fullscreen mode Exit fullscreen mode

Now if you test your current code, you should see the validation as well as the colored error message or success box.

Creating Your reCAPTCHA V3 Keys

Sign in or create a new Google reCAPTCHA Admin Console account. You can use the following link to do so: https://www.google.com/recaptcha/admin

Once there, you can create a new V3 reCAPTCHA site by clicking the + sign. Make sure you use reCAPTCHA V3. Complete the fields and you are good to go. Once completed, you should be presented with a site and private key. If not, you can open your site and click the cog menu item on the top right.

Adding reCAPTCHA to Your Website

In your HTML, right above the init.js reference, add the following code, replace yoursitekey with your actual site key from above:

<script src="https://www.google.com/recaptcha/api.js?render=yoursitekey"></script>
Enter fullscreen mode Exit fullscreen mode

Within your init.js, add the following code on the line after document.querySelector("#alert").innerText = "Processing your submission, please wait..."

Replace yoursitekey with your actual site key from above.

grecaptcha.ready(function() { // Wait for the recaptcha to be ready
                grecaptcha
                    .execute("yoursitekey", {
                        action: "contact"
                    }) // Execute the recaptcha
                    .then(function(token){

                        let recaptchaResponse = document.getElementById("recaptchaResponse")
                        recaptchaResponse.value = token // Set the recaptcha response

                        fetch("/send.php", {
                            method: "POST",
                            body: new FormData(form), // Send the form data
                        })
                            .then((response) => response.text())
                            .then((response) => {
                                const responseText = JSON.parse(response) // Get the response
                                if(responseText.error !== "") { // If there is an error
                                    document.querySelector("#alert").innerText = responseText.error
                                    document.querySelector("#alert").classList.add("error")
                                    document.querySelector(".formfields").style.display = "block"
                                    return
                                }
                                document.querySelector("#alert").innerText = responseText.success
                                document.querySelector("#alert").classList.add("success")
                                window.location.replace("/thanks") // Redirect to the thanks page
                            })
                    })
            })
Enter fullscreen mode Exit fullscreen mode

Your new, complete JS file should look like this:

window.addEventListener("load", function(){ // Wait for the page to load

    "use strict"; // Strict mode for JavaScript

    const form = document.querySelector(".contact") // Get the form

    form.addEventListener("submit", function (event){
        event.preventDefault() // Prevent the default action of the form
        let fields = document.querySelectorAll(".contact .form-control") // Get all the fields
        let valid = true
        for (var i = 0; i < fields.length; i++) {
            fields[i].classList.remove("no-error") // Remove the no-error class from all fields
            if(fields[i].value === ""){ // If the field is empty
                fields[i].classList.add("has-error")
                fields[i].nextElementSibling.style.display = "block"
                valid = false
            }else{ // If the field is not empty
                fields[i].classList.remove("has-error")
                fields[i].classList.add("no-error")
                fields[i].nextElementSibling.style.display = "none"
            }
        }
        if(valid){ // If all the fields are valid
            document.querySelector(".formfields").style.display = "none"
            document.querySelector("#alert").innerText = "Processing your submission, please wait..."
            grecaptcha.ready(function() { // Wait for the recaptcha to be ready
                grecaptcha
                    .execute("yoursitekey", {
                        action: "contact"
                    }) // Execute the recaptcha
                    .then(function(token){

                        let recaptchaResponse = document.getElementById("recaptchaResponse")
                        recaptchaResponse.value = token // Set the recaptcha response

                        fetch("/send.php", {
                            method: "POST",
                            body: new FormData(form), // Send the form data
                        })
                            .then((response) => response.text())
                            .then((response) => {
                                const responseText = JSON.parse(response) // Get the response
                                if(responseText.error !== "") { // If there is an error
                                    document.querySelector("#alert").innerText = responseText.error
                                    document.querySelector("#alert").classList.add("error")
                                    document.querySelector(".formfields").style.display = "block"
                                    return
                                }
                                document.querySelector("#alert").innerText = responseText.success
                                document.querySelector("#alert").classList.add("success")
                                window.location.replace("/thanks") // Redirect to the thanks page
                            })
                    })
            })
        }
    })
})
Enter fullscreen mode Exit fullscreen mode

Once you have made it here, we can get to work on the send.php file that will be used to get your reCAPTCHA score and process your form.

Once the form is validated and submitted, it will redirect to a thanks.html page if you included that in the structure above.

Validating reCAPTCHA Using PHP

In order to secure your website and protect from bots and spam, you need to hit Google's API server and validate what is being sent. Google does not give too much info away about how to process spam requests but we know a score > 0.5 is usually enough to prevent spam and bot submissions. So we will code for that metric.

This PHP file will double-verify that your required form elements have been submitted, then it will run those elements against Google's reCAPTCHA API to give you a score. Once you have your score you can process your email.

NOTE: Please change yoursecretkey to your actual secret key from above.

<?php
/**
 * Check to see if all fields that are required have been submitted
 *
 * @return boolean
 */
function isValid(){
    if(
        $_POST['fname'] != '' &&
        $_POST['lname'] != '' &&
        $_POST['email'] != '' &&
        $_POST['message'] != ''
    ) {
        return true;
    }
    return false;
}

// Declare variables to prepare for form submission
$success_output = '';
$error_output = '';

if (isValid()) {
    $recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify'; // URL to the reCAPTCHA server
    $recaptcha_secret = 'yoursecretkey'; // Secret key
    $recaptcha_response = $_POST['recaptchaResponse']; // Response from reCAPTCHA server, added to the form during processing
    $recaptcha = file_get_contents($recaptcha_url.'?secret='.$recaptcha_secret.'&response='.$recaptcha_response); // Send request to the server
    $recaptcha = json_decode($recaptcha); // Decode the JSON response
    if($recaptcha->success == true && $recaptcha->score >= 0.5 && $recaptcha->action == "contact"){ // If the response is valid
        // run email send routine
        $success_output = 'Your message was sent successfully.'; // Success message
    }else{
        $error_output = 'Something went wrong. Please try again later'; // Error message
    }
}else{
    $error_output = 'Please fill out all of the required fields.'; // Error message
}
// Output error or success message
$output = [
    'error' => $error_output,
    'success' => $success_output
];

// Return the output in JSON format
echo json_encode($output);
Enter fullscreen mode Exit fullscreen mode

This PHP file will return a JSON object that will be used in your JS file above to either display an error or display a success or in our case, redirect to a thank you page.

NOTE: In the code, I have left you space to add your own mail function. It is denoted like this:
// run email send routine

Remove that line and add your PHP mail routine. Typically, it would be like mail($to, $subject, $message, $headers);. If you need help with this, you can visit PHP's website for more details: https://www.php.net/manual/en/function.mail.php

Last Steps

When you add Google's V3 reCAPTCHA to your website, you will notice a small badge on the bottom right. This is not required if you follow the next few steps, but you need to either have it or display a message.

In your HTML, add the following code right below your button.

<br>
<br>
<p class="text-center">
    <small>This site is protected by reCAPTCHA and the Google <a href="https://policies.google.com/privacy">Privacy Policy</a> and <a href="https://policies.google.com/terms">Terms of Service</a> apply.</small>
</p>
Enter fullscreen mode Exit fullscreen mode

Now that you have the required message, you can add this to your Sass and remove the badge.

.grecaptcha-badge {
    display:none;
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Now that you have all of the pieces in place, you can refresh your page and test your form. Everything should work as expected and you can now be bot-free.

Discussion (0)