Verify Invisible reCAPTCHA in WordPress with AJAX

While researching to see if there was a good solution to verifying Invisible reCAPTCHA in WordPress, I came across several solutions that didn’t quite fit the bill for me. There are some plugins that will do this, for instance, but they generally hook into existing forms like the login and registration pieces. In my case, I had a form that lives on a page in WordPress, but is sending its information off to a 3rd party CRM that lives elsewhere. I came across Andrew Cross’s solution, which was pretty close to what I needed, and what follows is my modified approach to his idea.

So, the problem. As stated, I had a non-WordPress-native form that sends data to a 3rd party system that is protected by Invisible reCAPTCHA. The form itself used a traditional POST method, though there’s plenty of JavaScript to go around for things like field validation and the reCAPTCHA support. Setting up reCAPTCHA itself is pretty trivial. In your form, you just need an element like this (and yes, the sitekey is totally fake here):

So the basics of turning this into a functional, client side test only requires that you include the Google API file on your page. For this, I go to my script file in my theme:

This ensures the JavaScript API file is included in my theme in the footer (that’s what the true boolean is doing). The final step for client side validation is to create a JavaScript function called recaptchaSubmit() to match up to the callback defined in my div from earlier. This is what reCAPTCHA will fire when it thinks you are a human on the client side of the form. Here’s what that looks like:

Now that we have the reCAPTCHA JavaScript available, the above code creates a function that will bind all the client-side activity together for us. As we mentioned above, we’ve called this recaptchaSubmit(), though you can call it anything you want as long as it matches the callback included in your <div data-callback>‘s value above. This function will send off an AJAX request to an endpoint that will fire server-side in our theme to verify the captcha. In this case, we’re going to use the ability to attach custom actions to /wp-admin/admin-post.php, rather than creating a custom page or anything like that. Here, we dispatch a request to tell WordPress to run our validate_captcha action, and we pass it the response value the reCAPTCHA JavaScript gave us. After that magic happens, our JavaScript cleans up (or shows an error in this case) the form activity based on what we find out from the verification. This represents the end of the ultimate verification chain of events.

So far we’ve taken care of the HTML markup and JavaScript for verification. The final piece of the puzzle is to create the code that runs server side to verify the reCAPTCHA response with Google, to ensure they aren’t trying to cheat it. First up, we add an action hook to admin_post_ in WordPress.

You can call your action whatever you like, just appending it after the underscore. To this action we bind a PHP function of the same name (this isn’t required, it just makes it easy to cross-reference in the future to know your _validate_captcha hook calls a validate_captcha() function).

Finally, this is all you need for a server side function to make the remote request to Google for verification. It takes the reCAPTCHA response we generated from the JS API, which we included in the AJAX earlier, and asks Google if it matches up with what they expect. For reference, the $_POST['captcha'] that we grab is what we sent in that AJAX POST from a couple code blocks up above. Based on the response we get from Google, we either send back a good JSON response, or we throw a 403 error (or whatever you feel is appropriate for the situation).

Boom, you now have a relatively easy reCAPTCHA implementation that works in WordPress for any form you want to apply it to. The meat and potatoes here is just creating the function hook you can send the AJAX request to, and using that to leverage get_file_contents() so that you aren’t leaving your private key out in the open in plain JavaScript.

Know of a better technique? Did I make a mistake? Let me know in the comments below. If this tutorial is helpful, feel free to let me know!