Form validation with Google invisible recaptcha

Obada Qawwas
in Javascript - PHP

A ready out of the box example for a powerful registration form that includes Bootstrap form validation, Google invisible recaptcha, Ajax request after recaptcha’s challenge succeed to validate it in the server-side.

Integrating Form validation with Google recaptcha is a great way to protect your site from spams and attacks and verify them as real humans.
Many bots nowadays will look hard for contact forms that donโ€™t use recaptcha or an equivalent and send thousands of messages in a second through these forms. This has a lot of negative effects on your server, website and your brain health, besides, your domain can get marked as spam, or being blocked from email servers, and many more. In this example, you will see how you can add an extra layer of security by only allowing verified humans to submit the form.

Features:

Bootstrap form validation.
Google invisible recaptcha.
Recaptcha PHP validation.
Ajax request on recaptcha challenge success.
Ajax form loader ๐Ÿ™‚
Responsive.

So how we can achieve all of these?

First, you should get a site-key and a secret-key from Google recaptcha’s website, you can get one from this link after you sign in with your Google account https://www.google.com/recaptcha
Save these two keys somewhere, we will go back to them later.

Then you need to integrate Google recaptcha in your webpage, insert the following code in yourtag, or in footer it will work too.Our Javascript function that will handle recaptcha’s work will be named initRecaptcha.


<script src="https://google.com/recaptcha/api.js?onload=initRecaptcha&render=explicit"></script>
Then get some Javascript form validation library

We used Bootstrap Validator in our example, you can get it from Bootstrap Form Validator, and also include it in your page somewhere.

We are using jQuery v1.10.2 in this example (There is no explanation or reason for using such an old version but we was using it in the project that this nugget was taken from).
Build your HTML form

Our form contains a lot of extra fields, as an example to make it easy to understand.


<form action="#" method="post" class="row onyx-gap">
    <div class="form-head col-12">
        <input type="text" readonly="readonly" value="ID: #54357782" class="permanent-id" name="permanent-id">
        <div class="validation-errors"></div>
    </div>

    <!-- Fields -->
    <div class="col-md-6 col-sm-12 form-group">
        <input type="text" 
               class="styled-form__input form-control" 
               placeholder="Type your fullname please..." 
               name="fullname" 
               minlength="2" 
               maxlength="15" 
               data-bv-notempty-message="Fullname field is required." 
               data-bv-stringlength-message="Please type a valid value." 
               required>
    </div>
    <div class="col-md-6 col-sm-12 form-group">
        <input type="password" 
               class="styled-form__input form-control" 
               placeholder="Type your password please..." 
               name="password" 
               minlength="6" 
               maxlength="15" 
               data-bv-notempty-message="Password field is required." 
               data-bv-stringlength-message="Please type a valid value." 
               required>
    </div>
    <div class="col-12 form-group">
        <input type="email" 
               class="styled-form__input form-control" 
               placeholder="Types your email please..." 
               name="email" 
               data-bv-notempty-message="E-mail field is required." 
               data-bv-emailaddress-message="Please type a valid email." 
               required>
    </div>
    <div class="col-md-6 col-sm-12 form-group">
        <input type="tel" 
               class="styled-form__input form-control" 
               placeholder="Type your telephone..." 
               name="telephone" 
               minlength="9" 
               maxlength="15" 
               data-bv-notempty-message="Telephone field is required." 
               data-bv-stringlength-message="Please type a valid telephone number." 
               required>
    </div>
    <div class="col-md-6 col-sm-12 select-wrapper">
        <select name="university">
            <option value="default">University</option>
            <option value="test">Test</option>
            <option value="test">Test</option>
            <option value="test">Test</option>
            <option value="test">Test</option>
        </select>
    </div>
    <div class="col-12 form-group">
        <label for="contract">
            <input type="checkbox" 
                   id="contract" 
                   name="contract" 
                   class="form-control" 
                   value="contract" 
                   data-bv-notempty-message="Lorem ipsum dolor sit amet field is required." 
                   data-bv-stringlength-message="Please check Lorem ipsum dolor sit amet." 
                   required> 
                Lorem ipsum dolor sit amet.
            </label>
    </div>
    <div class="col-md-6 col-sm-12">
        <label for="sms-alert">
            <input type="checkbox" 
                   id="sms-alert" 
                   name="sms-alert" 
                   value="sms-alert"> 
            Quisque ullamcorper.
        </label>
    </div>
    <div class="col-md-6 col-sm-12">
        <label for="email-alert">
            <input type="checkbox" 
                   id="email-alert" 
                   name="email-alert" 
                   value="email-alert"> 
                Nunc suscipit erat.
            </label>
    </div>
    <div class="col-12">
        <div class="recaptcha"></div>
    </div>
    <div class="col-12">
        <button type="submit" name="submit" class="btn-icon">Continue</button>
    </div>
</form>
Why our form looks complex?

The .loading-container div is for the loader that we will show after sending the Ajax request, we were trying to be more user-friendly ๐Ÿ™‚
.form-head contains a readonly input, this one was for testing purposes, and it will contain the form validation error.

Each field you want to validate should have .form-control class and required attribute.
and of course we need some container for our Google recaptcha, in our example it has #form-recaptch ID, this ID will be used in the Javascript.

Now we’re ready to go to the Javascript part


var container = $('form'),
	limitRuns = 0;

container.bootstrapValidator({
	container: container.find('.validation-errors')[0], // Select the messages container
	feedbackIcons: {
		valid: 'fa fa-check-circle',
		invalid: 'fa fa-exclamation-circle',
		validating: 'fa fa-refresh'
	},
	live: 'submitted',
	onError: function(e) {
		container.find('.validation-errors').stop(0,0).slideDown(500,function(){
			$(this).css('height','auto');
		});
	},
    onSuccess: function(e) {

    	// Bootstrap validation was running success three time so I had to limit it this way
		if ( limitRuns == 0 ) {

			if ( grecaptcha.getResponse() !== 0 )
				grecaptcha.execute(); // Start the Google recaptcha

			limitRuns++;
		}
    }
});

If you’re wondering why we put limitRuns, it’s because for some mysterious reason the Bootstrap Validation was running the onsuccess event multiple times!!
Now we’re calling grecaptcha.execute to start the recaptcha after making sure it’s ready grecaptcha.getResponse and after form validation success.
and of course, the onerror event will show validation errors in the div that we prepared in the HTML form.

The most exciting part ๐Ÿ˜€


/**
 * Init recaptcha
 */
if (typeof grecaptcha !== 'undefined') {

	console.log('Recaptcha is here');

	var reCaptchaIDs = [];

	var initRecaptcha = function () {
		jQuery('.recaptcha').each(function (index, el) {
			var container = jQuery(this).parents('form');
			var tempID = grecaptcha.render(el, {
				'sitekey': 'YOUR_SITE_KEY',
				'theme': 'light',
				'badge': 'inline',
				'size': 'invisible',
				'callback': function (token) { // We may need the token later, who knows!
					globalFormsAjax(token, container);
				}
			});
			reCaptchaIDs.push(tempID);
		});
	};

	//Reset reCaptcha
	var recaptchaReset = function () {
		if (typeof reCaptchaIDs != 'undefined') {
			var arrayLength = reCaptchaIDs.length;
			for (var i = 0; i < arrayLength; i++) {
				grecaptcha.reset(reCaptchaIDs[i]);
			}
		}
	};
}


/**
 * The callback
**/
var globalFormsAjax = function(token, container){

	$.ajax({
		url: "recaptcha.php", // Validate the recaptcha challenge
	    method: "POST",
		dataType:'json',
	    data: container.serialize(),
	    beforeSend: function(xhr) {
			// Show our loader
			container.append('<div class="loading-container"><div class="loading-spinner"><div class="circle_01"></div><div class="circle_02"></div><div class="circle_03"></div><div class="circle_04"></div><div class="circle_05"></div><div class="circle_06"></div><div class="circle_07"></div><div class="circle_08"></div></div></div>');
			container.addClass('ajax-loader');
		},
		success: function(responseObj){
			// Stop the loader
			container.removeClass('ajax-loader');
			container.children('.loading-container').remove();

			// Show error message - Messages are in the PHP functions
			if ( responseObj.status == "success" ) {

				// Now we are OK to submit our form
				//container.submit();

				// Show a success message (just for testing)
				container.find('.validation-errors').html(responseObj.msg).stop(0,0).slideDown(500,function(){
					$(this).css('height','auto');
				});

				// If you want to show thank-you page
				// window.location.href='thank-you';

			} else {
				// Show the error message
				container.find('.validation-errors').html(responseObj.msg).stop(0,0).slideDown(500,function(){
					$(this).css('height','auto');
				});
			}

			// Reset recaptcha challenge if it's here
			if (typeof grecaptcha !== 'undefined') {
				recaptchaReset();
			}

			// Reset the form fields
			resetForm(container);
		}
	});
}


/**
 * Reset form fields
**/
var resetForm = function ( $form ) {

	// Reset the error message
	/*$form.find('.validation-errors').stop(0,0).slideUp(500,function(){
		$(this).css('height','auto').html('');
	});*/

	$form.find('input:not([readonly]), select, textarea').val('');
	$form.find('input:radio:not([readonly]), input:checkbox:not([readonly])').removeAttr('checked').removeAttr('selected');
	$form.find('input:text, input:password, input, input:file, select, textarea, input:radio, input:checkbox').parent().find('.form-control-feedback').hide();
	$form.find('.has-feedback').removeClass('has-feedback');
	$form.find('.has-success').removeClass('has-success');
};

You can read about the grecaptcha.render options on their website https://developers.google.com/recaptcha/docs/invisible

Don’t forget to replace YOUR_SITE_KEY with the site-key you got from Google.
Now our great recaptcha will call recaptchaFormsAjax when it runs.
In that function, we’re sending an Ajax request to check and validate the recaptcha challenge in PHP.


/**
 * Google recaptcha server side check
 */

$captcha;

if ( isset($_POST['g-recaptcha-response']) ) {
    $captcha = $_POST['g-recaptcha-response'];
}

if( !$captcha ){
    $response = array (
        'status' => 'error',
        'msg' => 'Please check the the captcha form.'
    );
    echo json_encode($response);
    exit;
}

$secretKey = "YOUR_SECRET_KEY";

$ip = $_SERVER['REMOTE_ADDR'];

$googleResponse =  file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=".$secretKey."&response=".$captcha."&remoteip=".$ip);

$responseKeys = json_decode($googleResponse,true);

if(intval($responseKeys["success"]) !== 1) {
    $response = array (
        'status' => 'spam',
        'msg' => 'You look like a spam.'
    );
    echo json_encode($response);
    exit;
} else {
    $response = array (
        'status' => 'success',
        'msg' => 'You\'re good to go.'
    );
    echo json_encode($response);
    exit;
}

Also, don’t forget to replace YOUR_SECRET_KEY with the secret-key you got from Goolge.
This function will validate the recaptcha challenge and return a message to our Javascript function to go on.

Validation screenshot:
Bootstrap form validation with Google invisible reCaptcha, Ajax request after challenge success and reCaptcha validation
New Version Notes

I’ve updated this snippet to make it more dynamic and supports multiple forms and recaptchas, also a new function to reset the form and the recaptcha challenge was added.
I commented the form submit part and the redirect to the thank-you page for demo purposes but please don’t forget to check it again.
Also, the loader is being added from the Javascript and removed after the Ajax request finish.

Finally

Download our working example or view demo from the buttons at the beginning of this post.
Please don’t hesitate to write to me if something went wrong with you ๐Ÿ™‚ I’m always glad to help.

References and Credits:

jQuery ๐Ÿ˜€
Bootstrap
Font Awesome
Montserrat font
Bootstrap Form Validator

Comments (34)
  • TuniRio
    Wrote
    March 23, 2018 at 12:12 pm

    This article is very helpful because those informations are needs for websites and Google recaptcha.

    Thank you for this article, I’m following you every time.

    • admin
      Replied to TuniRio
      March 23, 2018 at 12:19 pm

      Thank you :), I’m glad you like it.

  • Chad
    Wrote
    June 20, 2018 at 4:08 pm

    Hey could you please look at my site. I am trying to use your form. Only thing I done was removed some fields and changed the css, for the colours. So everything looks good, but when I click “Submit”, which I changed from “Continue”. Nothing happens? http://cdesignc.co.za/

    • admin
      Replied to Chad
      June 25, 2018 at 11:47 am

      Hello,
      Sorry for the delay, I’ve been busy lately.
      I looked at your website but I didn’t understand why you made the form through an Iframe!
      and this form is integrated with Google Recaptcha, I think you removed the Google Recaptcha part and the form is giving an error.
      contact me through my email if you didn’t solve it.
      info[at]onyxdev.net

      Regards

  • svuk
    Wrote
    August 15, 2018 at 5:13 am

    Hello, I really liked your implementation of the invisible recaptcha, there is such a task: I need to have two invisible recaptchas on the page (I’m new to this business, and I can not do it)
    I really need help.
    Thank you in advance

    • admin
      Replied to svuk
      August 15, 2018 at 6:18 am

      Hello,
      I’m happy that you like it, no problem, send me your website details and I hope I can help ๐Ÿ™‚

  • Prashanth Rajasekaran
    Wrote
    September 11, 2018 at 9:38 am

    Great Work Thank you so much.

    • admin
      Replied to Prashanth Rajasekaran
      September 11, 2018 at 10:04 am

      You’re welcome ๐Ÿ™‚

  • Highly recommended Online site
    Wrote
    November 22, 2018 at 1:31 pm

    I just could not go away your site before suggesting that I actually enjoyed the standard info a person supply in your guests? Is going to be back continuously to investigate cross-check new posts

    https://www.animatron.com/studio/users/broadcastbike

    • admin
      Replied to Highly recommended Online site
      November 23, 2018 at 7:09 am

      I’m glad you like it, you’re welcome ๐Ÿ™‚

  • 01K
    Wrote
    December 1, 2018 at 2:04 pm

    By they way. If form validation fails – the submit button will be disabled. Then you correct form, but button is still disabled. So you should change the add something or change in a form to execute validation once more.
    Otherwise button will be disabled. Any workaroud?

    • admin
      Replied to 01K
      December 3, 2018 at 8:59 am

      If you filled all the required fields the button will be enabled… check that there are no validation errors above the form in the validation messages area… you can send me your project’s URL and I will be glad to help ๐Ÿ˜‡

  • Panlasang Pinoy
    Wrote
    April 9, 2019 at 1:34 pm

    Thanks for sharing this. I will implement this to my project,

    • admin
      Replied to Panlasang Pinoy
      April 9, 2019 at 1:41 pm

      You’re welcome ๐Ÿ˜Š

  • April 10, 2019 at 8:58 pm

    Enjoyed the post.

    • admin
      Replied to boekhouding zzp breda
      May 2, 2019 at 12:07 pm

      You’re welcome, I’ve updated it, it’s more dynamic now ๐Ÿ˜‡

  • Couperose
    Wrote
    April 18, 2019 at 5:03 am

    Looks realy great! Thanks for the post.

    • admin
      Replied to Couperose
      May 2, 2019 at 12:14 pm

      You’re welcome, I’m glad you like it

  • sri
    Wrote
    April 19, 2019 at 10:07 pm

    HI I used invisible recaptcha while submitting a form. Sometimes the form is getting submitted and sometimes it is showing error. When I checked the network through developer tools, whenever the form is taking time to submit it is throwing error. Can you please help me with that?

    • admin
      Replied to sri
      May 2, 2019 at 12:17 pm

      Hi sri,
      I’ll be glad to help you, please send me a screenshot for the error you’re getting in the console

  • April 21, 2019 at 3:19 am

    Thanks for this nice post. …

    • admin
      Replied to huidverzorging
      May 2, 2019 at 12:05 pm

      You’re welcome, I’m glad you like it.
      It has been updated now if you want to stay up to date download the files again ๐Ÿ˜‡

  • Shabu
    Wrote
    May 1, 2019 at 1:21 pm

    Thanks for sharing a great post. However i need a improvement. How to reset the form after successful submission is there any idea

    • admin
      Replied to Shabu
      May 2, 2019 at 12:04 pm

      Hey Shabu!
      I’ve made a new version and updated the files, please download it again and follow up the comments I’ve written along the way, it’s now more dynamic ๐Ÿ˜Ž

  • mace
    Wrote
    June 12, 2019 at 12:04 pm

    Hi, how do i post the form to my email? Ive looked everywhere for a mail address to add to the code somewhere? Thanks

    • admin
      Replied to mace
      June 12, 2019 at 1:32 pm

      Hello Mace,
      I’m sorry for this but this example doesn’t include email sending function ๐Ÿ˜”, I have another example that sends emails through PHPMailer but I didn’t have the time to put it here, I can send it to you by mail and you can figure it out ๐Ÿ˜‡

  • Gerson
    Wrote
    July 1, 2019 at 10:34 pm

    How can I add the email to the one that will be sent? Thanks

  • Heero
    Wrote
    July 25, 2019 at 12:56 pm

    Hello,
    thank you for your helpful great work, im trying to use it, everything seems oky, but when i submit i get : you seem like a spam, any idea? my envirement is : iis 8.5, php 5.6, hsts enabled, https too, thank you

    • admin
      Replied to Heero
      July 25, 2019 at 1:03 pm

      Hello Heero,
      You’re welcome, I’m glad you liked my article.
      This message means that a falsy response is coming from Google, are you sure you’ve replaced YOUR_SECRET_KEY with your key?

    • Heero
      Replied to admin
      July 25, 2019 at 1:49 pm

      thank you for your replay :
      yes my secret key in the post php, my site key in the script js

    • admin
      Replied to Heero
      July 25, 2019 at 1:50 pm

      Can it be that your hosting is blocking the outgoing connections?
      if itโ€™s not the problem, send me your website details by email and Iโ€™ll try to help ๐Ÿ™‚

    • Heero
      Replied to admin
      July 25, 2019 at 2:54 pm

      true, our website allow by country access in iis server, and file robot.txt block crawling

    • admin
      Replied to Heero
      July 25, 2019 at 2:58 pm

      The robots.txt doesn’t affect the outgoing connections.
      BTW, why you’re trying to use recaptcha with an IIS?!
      Is it working now?

Please enable Javascript in order to post comments.