Google Invisible reCaptcha – How To Boost Lighthouse Performance Score?

This article is about reCaptcha v2.0 API with explicit rendering method integration — it is not intended for reCaptcha v3.0

Did you know that Google’s reCaptcha anti-spam system SLOWS DOWN all of your websites?

Google reCaptcha Logo

Google reCaptcha Logo

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.

Google Lighthouse And reCaptcha Artwork

Google Lighthouse And reCaptcha Artwork

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.

Google Lighthouse Page Performance with-without reCaptcha

Google Lighthouse Page Performance with-without reCaptcha

Google Lighthouse Page Diagnostics with-without reCaptcha

Google Lighthouse Page Diagnostics with-without reCaptcha

Google PageSpeed Insights Score - with+without reCaptcha

Google PageSpeed Insights Score – with+without reCaptcha

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! :)

Comments


  1. comments

    18 Comments

    Add Your Comment
  2. 1. mahesh bhosale

    third option is not working, i am getting spams
    i did exactly as showing in steps it helps for speed but main moto of ReCaptcha is not working as expected , please help

  3. 2. TehnoBlog (In reply to mahesh bhosale)

    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:

    invisible_recaptcha folder > engines > PublicEngine.php

    look inside method:

    public function enqueuePublicScriptsAndStyles() {
    ...
    }

    replace 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');

    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.

  4. 3. Alex

    Hi! 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:

    (link to image removed)6

  5. 4. TehnoBlog (In reply to Alex)

    Hi 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.

  6. 5. Jek

    How to do it with version 3 reCaptcha on the site? Not WordPress, static site?

  7. 6. TehnoBlog (In reply to Jek)

    reCaptcha v3 works entirely differently, and you can’t use this method.

  8. 7. Guest (In reply to TehnoBlog)

    Is there a method that can be used?

  9. 8. TehnoBlog (In reply to Guest)

    No. v3 must be loaded on every page. That’s how it works.

  10. 9. Bojan (In reply to TehnoBlog)

    Many 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?

  11. 10. TehnoBlog (In reply to Bojan)

    Hi, 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.

  12. 11. Guest

    Really 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?

  13. 12. TehnoBlog (In reply to Guest)

    One 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.

  14. 13. Anuj

    Will 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.

  15. 14. TehnoBlog (In reply to Anuj)

    Hi, 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.

  16. 15. Tester

    While 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.

  17. 16. TehnoBlog (In reply to Tester)

    Hi, 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).

  18. 17. Alessandro S.

    Hello I’m trying to make it working for my wordpress Enfold theme site.
    I tried with standard contact of enfold (ajax) and nothing happens
    I tried with CF7 and nothing happens. It’s not clear how it works

  19. 18. TehnoBlog (In reply to Alessandro S.)

    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!

Post A Comment

I have read and consent to Privacy Policy and Terms and Conditions