Invisible reCAPTCHA 101
What is Invisible reCAPTCHA, and why use it?
As most have probably already seen, Google has something called reCAPTCHA that is designed to prevent spambots from submitting tons of garbage to your form postback processor. It’s easily recognizable as the checkbox with the label “I’m not a robot”. Though it is effective, it has some drawbacks.
The typical appearance of Google’s reCAPTCHA widget.The first drawback is that it’s ugly, and takes up space in your form. The second is that studies indicate users don’t like it, as much as 30% of users bail when they see it. But the third and most problematic for web developers is that it renders in an iframe, which can break your page design; you simply cannot easily and consistently style the thing. The only configuration option for the control’s size is a data-size parameter, with the only possible values of ‘normal’ or ‘compact’. These render at 300px and 160px wide respectively, so if those widths aren’t going to look good with your layout, you’ll have to resort to hacks using transform: scale.
reCAPTCHA ‘compact’ rendering, which does not seem so compact. In fact, it looks like they just reduced the width and applied it to the height!
Things get worse if you have a responsive design. You’ll have to resort to using javascript and attach a window resize event handler and then compute and set the transform width. Even then you’ll run into issues with the height, because transform happens after the render, so the height will render outside of the normal flow of the document. Anyway, trust me when I say it’s a nightmare to try and get it to render outside of the two predefined sizes, and I could never get it to render in a satisfactorily responsive way for mobile.
But as I was researching the different parameters I noticed this ‘invisible’ version Google was offering. I was skeptical because of my experience with the standard reCAPTCHA, but I gave it a shot and I’m glad I did.
The whole idea behind it is that it’s invisible to the user, which is kind of true. It uses the submit button click as the proof that you’re a human, but as we all know a computer can be programmed to press buttons on the screen. Now, if you’re a legit human and submit the form once from your IP address, that will probably be the end of it. But if Google suspects any shenanigans, a popup will appear with the image grid asking you to click storefronts and buses.
The best thing is that the infernal control with the checkbox goes away and you don’t have to worry about it screwing up your design. There is a caveat though, which is that Google still wants to let your users know your form is using reCAPTCHA and to display their Privacy and Terms links. They do this by adding an expanding fixed position notification to the bottom right of your page.
Invisible reCAPTCHA’s default notification, collapsed and expanded (on hover).
By default it appears in the lower right of the browser and because it’s fixed, it’s always visible. This is pretty annoying and basically is unusable in mobile because it takes up too much real estate. So again, we have a design problem. Google does give you the option to put the notification ‘inline’, but it will appear right above your submit button and for the love of Pete I don’t know why, it’s an iframe so you run into the same exact issues as the standard checkbox version.
So what to do? Well, Google requires you to display the notification in their terms of service, and they do deserve credit for the free use of their technology, so we cannot simply hide the notification… or can we? As much of life is, I wound up compromising. I DID hide their iframe using display:none, but replaced it with a custom notification box of my own using their logo and Privacy and Terms links. This way I have complete control over how it renders and adhere to their terms of use. Here is modified version of their notification I created:
Small but noticeable, compact and truly able to style/resize in any way so it work with any design.
So there you have it, we can use Invisible reCAPTCHA to overcome the design issues, but are there any issues with implementation? Of course there are!
Implementation
Get your DEDICATED invisible reCAPTCHA key
First thing’s first, you need to get your reCAPTCHA site and secret keys. Now you’d think if you already have a v2 “I’m not a robot” checkbox reCAPTCHA key, that you could just use that for the invisible reCAPTCHA. Well, you can’t. You must create a dedicated key just for invisible reCAPTCHA. To create such a key, log into your Google account and go here: https://www.google.com/recaptcha/admin#list
On that page you’ll see the “Register a new site” heading. Make sure you specify “Invisible reCAPTCHA” when creating the key.
Basic Implementation
Google’s documentation (https://developers.google.com/recaptcha/docs/invisible) lists the three ways you can implement Invisible reCAPTCHA. The first is the easiest and binds it to your submit button, but unfortunately, it’s basically useless if you want to use HTML 5 validation. Modern browsers support HTML 5 validation, which is pretty robust and saves a lot of time. But the thing with the button binding implementation is that it takes over the button and seemingly transforms the button into a regular button and no longer a submit button recognized by the browser, thus it does fire off the HTML 5 validation.
Now you can do your own javascript validation, but that’s no fun and adds back a lot of extra work that HTML 5 validation relieved us of.
Advanced Implementation
Luckily Google provides a way to bind to a div, so it will leave your submit button alone. This is probably what you’re going to want to use. But it’s a bit tricky to get working, and I use jQuery to assist.
Let’s jump straight to the code, which I’ve documented copiously, so you can see what’s going on:
<script type="text/javascript">// this is the handle to the recaptcha div elementvar div_recaptcha; // this is a callback that is executed when the recaptcha script loads// you can specify an onload callback in the script tag’s src:// e.g. src="https://www.google.com/recaptcha/api.js?onload=invRcOnLoad...// see further below for full <script> tagvar invRcOnLoad = function() { // here the recaptcha div is initialized and the handle returned // and assigned to the div_recaptcha var. // * the first arg is the recaptcha div’s id // * 'callback' is a function that is called after the recaptcha has // been successfully completed and the recaptcha challenge code has // been received. // * 'size' tells the renderer what type of recaptcha to use div_recaptcha = grecaptcha.render('div-recaptcha', {'sitekey' : '<YOUR_SITE_KEY>', 'callback' : recaptchaCallback, 'size': 'invisible'});};// create submit event listener that checks if the recaptcha has been executed. // we can use 'getResponse' to see if it’s been executed. If it has not, then// preventDefault (prevent form from being submitted so we can execute the recaptcha.// note: the jQuery listener only fires if html5 validation passes, which is good!jQuery('#my-form').submit(function(event) {if (!grecaptcha.getResponse(div_recaptcha)) { event.preventDefault(); grecaptcha.execute(div_recaptcha);}});// and here is the recaptchaCallback which executes when the recaptcha has been // successfully completed.recaptchaCallback = function() { jQuery('#my-form').submit();}</script><!-- include the external Google reCAPTCHA code. Note the 'onload' and 'render' params --><script src="https://www.google.com/recaptcha/api.js?onload=invRcOnLoad&render=explicit" async defer></script><!-- and finally add this in the body of your form. It will host the recaptcha form element --><form id='demo-form' action="?" method="POST"><div id="div-recaptcha"></div></form>
Postback Processing
What good is reCAPTCHA if you don’t validate it in postback? Here is my PHP validation method:
Conclusion
To recap, we leave “I’m not a robot” behind, create a custom recaptcha notification that we can style as we please, and add custom implementation to retain HTML 5 form validation. All together it provides a solid solution that allows maximum flexibility in the design and functionality of your forms.
$rc_response = $_POST['g-recaptcha-response'];$rc_secret = <YOUR_SECRET_KEY>;$passed = isRecaptchaValid($rc_response, $rc_secret);function isRecaptchaValid($rc_response, $rc_secret) { if( ! $rc_resp) { return false; } $ip = $_SERVER['REMOTE_ADDR']; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => 'https://www.google.com/recaptcha/api/siteverify', CURLOPT_POST => true, // the request MUST be POST CURLOPT_POSTFIELDS => [ 'secret' => $rc_secret, 'response' => $rc_response, 'remoteip' => $ip], CURLOPT_RETURNTRANSFER => true]);$site_verify = curl_exec($ch);curl_close($ch);$sv_data = json_decode($site_verify, true);return (intval($sv_data["success"]) === 1);}