Did you know that Google’s reCaptcha anti-spam system SLOWS DOWN all of your websites?
If you don’t know what Lighthouse is check our brief introduction to Lighthouse project.
Google Invisible reCaptcha + Lighthouse Performance Score
Recently, we were working on speed optimization and come to some very interesting findings during our website performance audit. One thing in particular that came as a real shocker was recaptcha__en.js resource that was listed as NO.1 culprit in our JS performance profiling section under Lighthouse audit session ($300 laptop). Over 2 seconds were spent just to evaluate that huge code base (api.js initiates recaptcha__en.js) and little over 50 ms to run it. The later part is not a big deal, but the former one is. Why? Because it affects Time to Interactive parameter in Lighthouse, apparently, and, that is a big deal.
To further verify these findings, we did a ON/OFF A/B testing with/without reCaptcha plugin enabled in WordPress, and it was clear as black on white that it is a big deal, after all. See screenshots below + summarized table with scores.
N | Audit Test | with reCaptcha plugin |
without reCaptcha plugin |
Score Difference |
---|---|---|---|---|
1 | Google Chrome Browser Lighthouse Performance Score |
52 | 63 | over 20% |
2 | Google PageSpeed Insights Performance Score |
24 | 38 | over 55% |
The above results are far from ideal, but can you notice (can you?) that single “thing” in form of a reCaptcha library contributing to a huge score penalty? What alternatives we have at our disposal to improve this awkward situation?
Well, for one, we have absolutely no intentions leaving our property unprotected. No, sir! On the other hand, we don’t wish to penalize every user/visitor of our website, who doesn’t want to leave comments or fill-in contact and registration forms, pushing towards it’s browser’s throat all protection stuff completely unnecessary!
Few things comes to mind:
IDEA #1
Get rid of reCaptcha for good! Yeah, easier said than done. Good luck with that approach! :)
IDEA #2
Separate forms with reCaptcha and load them via Ajax only when user decides to interact with them (e.g. click to show/load comments and new comment submit form button). This sounds sound, but how do we actually achieve it? Well, it’s complicated, really. It sounds simple, but in reality it’s not. Particularly, if you don’t know JavaScript and PHP inside-out and if you aren’t prepared to rewrite huge portions of your WordPress theme, than you are on your own. Oh, btw, good luck googling hands-on easy-to-follow tutorials about this.
IDEA #3 – BINGO!
Forget about Ajax splitting, keep things just as they are, and trigger loading/execution/running of reCaptcha library only when user interacts with the input form(s). This is, actually, quite possible and relatively easy to implement!
Let’s do it!
Google reCaptcha Manual Loading On Custom Trigger Event
Ideally, we should trigger Google reCaptcha loading only when users interacts with our forms – e.g. when the form is in focus, or when user starts typing into the forms. While they are typing their nonsense, reCaptcha will silently load in the background in a split of a second, starting it’s protective role on our form(s), but, not before that event!
PART 1: HTML Input Form
HTML – simplest WordPress comment submit form:
<form id="commentform" action="./wp-comments-post.php" method="post">
<input id="author" name="author" type="text" aria-label="author-name" />
<textarea id="comment" name="comment" aria-label="comment-text"></textarea>
<input id="submit" class="invisible-recaptcha" name="submit" type="submit" value="Submit" aria-label="submit-comment" />
</form>
PART 2: reCaptcha div holder
This is usually injected by WP plugins, but here it is for completeness:
<div class="invisible-recaptcha"></div>
PART 3: reCaptcha Explicit Render
Important part here is to remove loading of https://www.google.com/recaptcha/api.js?… script right away in the header – that’s a big no-no now! If you use WordPress or other CMS plugins, you should modify plugin’s enqueue function.
Example universal reCaptcha explicit render implementation for WordPress (note: see invisible-recaptcha plugin > PublicEngine.php file for more details).
Note: We have greatly simplified below code, because in newer plugin versions support was added for Contact Form 7 (CF7) plugin as well, which is not relevant in this case.
<script type='text/javascript'>
function renderReCaptcha() {
for (var i = 0; i < document.forms.length; ++i) {
var form = document.forms[i];
var holder = form.querySelector('.invisible-recaptcha');
if (null === holder) continue;
(function(frm){
var holderId = grecaptcha.render(holder, {
'sitekey': 'MY-API-KEY-HERE',
'badge': 'inline', // or 'badge'
'size': 'invisible',
'callback': function (recaptchaToken) {HTMLFormElement.prototype.submit.call(frm);},
'expired-callback': function(){grecaptcha.reset(holderId);}
});
frm.onsubmit = function (evt){evt.preventDefault();grecaptcha.execute(holderId);};
})(form);
}
};
</script>
PART 4: reCaptcha Load
Bind reCaptcha to form input field focus event listener.
This is the most important part that makes the whole magic fully functional! ;)
Of course, here’s the part where you can get creative. For example, you can initially hide comment input form and place a button named ‘Leave comment’ or similar. When user clicks on that button, it will reveal comment input form, and load reCaptcha library simultaneously. Naturally, code will require some modification e.g. to change event listener from ‘focus’ to ‘click’ event and target elements (tags), but everything else should remain essentially the same.
JavaScript solution
<script type="text/javascript">
// trigger loading api.js (recaptcha.js) script
var reCaptchaFocus = function() {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://www.google.com/recaptcha/api.js?onload=renderReCaptcha&render=explicit';
head.appendChild(script);
// remove focus to avoid js error:
// Uncaught Error: reCAPTCHA has already been rendered in this element at Object.kh
document.getElementById('author').removeEventListener('focus', reCaptchaFocus);
document.getElementById('comment').removeEventListener('focus', reCaptchaFocus);
};
// add initial event listener to our basic HTML form
document.getElementById('author').addEventListener('focus', reCaptchaFocus, false);
document.getElementById('comment').addEventListener('focus', reCaptchaFocus, false);
</script>
jQuery solution (partial)
<script type="text/javascript">
$('#author,#comment').on('focus', function() {
// trigger loading api.js (recaptcha.js) script
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://www.google.com/recaptcha/api.js?onload=renderReCaptcha&render=explicit';
head.appendChild(script);
// remove focus to avoid js error:
// Uncaught Error: reCAPTCHA has already been rendered in this element at Object.kh
$('#author,#comment').off('focus');
});
</script>
AND DONE!
That’s the whole trick, really! We improved our website score by some 20% in Chrome’s v71 Lighthouse, and 55% in Google’s PageSpeed insights online test suite (mobile emulation mode in both cases). Plus, we will not load reCaptcha to visitors who will never interact with our forms in the first place! Ok, score of 38 in PageSpeed still sucks (unusually high interaction time could be a bug on their end or they are using like really slow ancient device), but, hey – it’s still an improvement! :)
18 Comments
Add Your Commentthird option is not working, i am getting spams
February 20th, 2019i did exactly as showing in steps it helps for speed but main moto of ReCaptcha is not working as expected , please help
Hi, this exact implementation is currently running on this very website and it is definitely working. Back-end side must be modified to avoid initial loading of reCaptcha on page load, as clearly mentioned right at the beginning of the section in the article titled PART 3: reCaptcha Explicit Render. We have omitted plugin modification side, because it is WordPress and particular plugin specific solution, not a universal one, as plugin may get updated and this mod no longer be valid.
Here is the modification part you must apply in above mentioned Invisible reCaptcha plugin for WordPress:
locate and open:
look inside method:
replace this line:
with following code:
And it will work. WordPress 5.0+ is required.
This modification is required because we need to remove wp_add_inline_script() dependency: enqueued reCapcha api.js script. This way we will 'trick' plugin to load explicit render code without api.js dependency being loaded and prevent reCaptcha run on initial page load.
February 20th, 2019Hi! Recaptcha don’t work. Help me please.
I replaced this line:
wp_enqueue_script('google-invisible-recaptcha', $googleApiUrl , array(), null, true);
with following code:
// register dummy script for wp_add_inline_script() function in order to inject inline explicit-render reCaptcha js code
wp_register_script('google-invisible-recaptcha', '', [], '', true);
wp_enqueue_script('google-invisible-recaptcha');
Then I added this code to the js:
April 6th, 2020(link to image removed)
6Hi Alex,
You must examine your console log, check javascript errors and make sure your theme is compatible with it, or adapt it to become compatible. This is only the example. Other plugins might interfere with the code, as well. Sorry, this goes beyond the simple troubleshooting scope, you can try posting on stackoverflow or wordpress.stackexchange.com your issue, but you must provide all the details there to avoid question getting closed.
April 6th, 2020How to do it with version 3 reCaptcha on the site? Not WordPress, static site?
June 29th, 2020reCaptcha v3 works entirely differently, and you can’t use this method.
June 29th, 2020Is there a method that can be used?
June 29th, 2020No. v3 must be loaded on every page. That’s how it works.
June 29th, 2020Many thanks for a great article. I’m working right now on an implementation of removing reCaptcha 2 on page load and the idea is to lazy load it in a similar way you did it. Now, could you please elaborate why is this not possible with reCaptcha 3 ? I understand that v3 score is calculated based on user interaction prior to execution, is this the reason why it cannot be prevented from loading before the execution?
July 9th, 2020Hi, exactly. reCaptcha v3 monitors entire user interaction with your website/app on all pages, not just once it presents the submit form.
You can use Google’s provided execute method example if you need more control when it runs, but beyond that not sure if it would be meaningful to modify it at all.
July 9th, 2020Really helped me. I have 2 forms in one page with different IDs but same sitekey. How can I do it with same script or should I use 2 scripts?
October 20th, 2020One sitekey is good enough (and even recommended) for a lot of different websites. No need to create new ones for each form / domain / subdomain / site, simply add them on Google’s reCaptcha Admin page.
In the past you could optionally activate domain check / filtering, in order to prevent abuse of your reCaptcha API instance, but this is now done by default on Google’s reCaptcha Admin setup page.
October 21st, 2020Will this successfully prevent the bot? Since the captcha script won’t get proper time to analyze the user interaction. I am very doubtful that this will prevent spam submission.
January 7th, 2021Hi, we haven’t noticed any substantial spam increase since the implementation of this system. Yes, few messages pass here and there, but don’t confuse it with version 3, which works differently, as you say.
January 7th, 2021While this “solution” is really good for the pagespeed index, it does nothing to hold back spam. Bots can easily bypass this. Tested on 6 sites.
January 28th, 2021Hi, I hope you correctly implemented backend captcha check. You can verify this simply by temporarily disabling JavaScript e.g. using DevTools* in Google Chrome desktop browser. If you can bypass your own website this way, then you definitely did something wrong.
Keep in mind that Google reCaptcha is not 100% bulletproof anti-bot protection. Unfortunately, there are ways to bypass it, as we have talked about how to bypass captcha in this article here.
* To launch DevTools in Google Chrome desktop browser click on vertical 3-dots Menu (top right corner) > More tools > Developer tools and set option to disable JavaScript (while developer tools window is active).
January 28th, 2021Hello I’m trying to make it working for my wordpress Enfold theme site.
February 20th, 2021I tried with standard contact of enfold (ajax) and nothing happens
I tried with CF7 and nothing happens. It’s not clear how it works
It works only with particular Invisible reCaptcha v2 plugin for WordPress mentioned in the article and it requires modification of the plugin, plus read comment reply above as additional code required for it.
Regarding Contact Form 7, CF7 plugin comes with a built-in Invisible reCaptcha v3 support, but that only works on contact forms, not for comments. As mentioned before, this modification is not possible neither required for reCaptcha v3.
This is a general article about advanced reCaptcha v2 implementation that gives developers an idea, it should be pretty clear with given information.
If you do not have a good knowledge of JavaScript and PHP, it is better to avoid changing anything, as improper modifications will render your captcha non-functional and either open your website to spammers or prevent contact and comment forms from working entirely!
February 20th, 2021