How to Add reCAPTCHA to Forms
The reCAPTCHA user-dialogue system has become almost ubiquitous with online forms. It helps protect web forms from spam and automated bots by creating tests that human users can easily pass but are bothersome to automated bots.
A reCAPTCHA can only be used once within any given page’s DOM, which can pose an issue when you want to secure multiple forms on the same page. Luckily, we can resolve this issue with some
jQuery UI, AJAX, and the
reCAPTCHA API. By mixing these key ingredients together, we can create a great user experience while securing any number of forms on a given page.
Now let’s get ourselves set-up for some fun!
After we’ve gotten our public and private API keys from reCAPTCHA, let’s start with a simple form in our HTML to run off of.
<form id="my_form" name="my_form" method="post">
<input type="submit" value="Submit My Form" />
</form>
You’ll notice that I did not use the [action] property on the form element. That’s because, we’re going to be using Javascript and jQuery’s .on() function to handle the submission. To get started, we will need to include jQuery UI and the reCAPTCHA AJAX API. Our HTML document should look something like this now.
<html>
<head>
<title>reCAPTCHA</title>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.0/themes/base/jquery-ui.css">
<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.0.js"></script>
<script type="text/javascript" src="http://code.jquery.com/ui/1.10.0/jquery-ui.js"></script>
<script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>
</head>
<body>
<form id="my_form" name="my_form" method="post">
<input type="submit" value="Submit My Form" />
</form>
<script>
$('form').on('submit', function(e){
e.preventDefault();
var formID = e.currentTarget.id;
alert(formID+' Form Submitted');
});
</script>
</body>
</html>
The reCAPTCHA AJAX API will allow us to display and control the reCAPTCHA via Javascript, and the jQuery UI will allow us to put that reCAPTCHA in a nice modal. First, we need a place for this modal’s content to live. To do that, we’re going to add a hidden <div> to the HTML content. Afterwards, we will replace our alert message with the jQuery UI dialog modal. Our HTML document should now look something like this:
<html>
<head>
<title>reCAPTCHA</title>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.0/themes/base/jquery-ui.css">
<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.0.js"></script>
<script type="text/javascript" src="http://code.jquery.com/ui/1.10.0/jquery-ui.js"></script>
<script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>
</head>
<body>
<form id="my_form" name="my_form" method="post">
<input type="submit" value="Submit My Form" />
</form>
<div id="recaptchaModal" title="reCAPTCHA" style="display:none;">
<p>Please fill out this reCAPTCHA to submit this form.</p>
<div id="recaptcha_block"></div>
</div>
<script>
$('form').on('submit', function(e){
e.preventDefault();
var formID = e.currentTarget.id;
$("#recaptchaModal").dialog({
modal: true,
resizable: false,
width: 400
});
});
</script>
</body>
</html>
We now have a working modal (HUZZAH!), but no reCAPTCHA yet. We can take care of that with a call to our reCAPTCHA AJAX API, which we will be putting into the “open” event callback of the jQuery UI Dialog options. When finished we will have something like this:
$("#recaptchaModal").dialog({
modal: true,
resizable: false,
width: 400,
open: function(e, ui){
Recaptcha.create("your_recaptcha_public_key",
"recaptcha_block",
{
theme: "red",
callback: Recaptcha.focus_resonse_field
}
);
}
});
Excellent! Our reCAPTCHA is now showing up inside of the dialog modal. We’re not done yet, though! We’re going to do some DOM cleanup, just in case the user decides to cancel out of the dialog modal for whatever reason.
$("#recaptchaModal").dialog({
modal: true,
resizable: false,
width: 400,
open: function(e, ui){
Recaptcha.create("your_recaptcha_public_key",
"recaptcha_block",
{
theme: "red",
callback: Recaptcha.focus_resonse_field
}
);
},
close: function(e, ui){
Recaptcha.destroy();
}
});
By doing this cleanup in the “close” callback, we will ensure that the reCAPTCHA challenge does not remain in the DOM indefinitely. As an added bonus, it also ensures that a unique reCAPTCHA challenge is presented every time the dialog modal is opened.
We’re on the home stretch now; time to think about submitting and validation of the reCAPTCHA. Let’s add some buttons to our dialog modal, so that we can tie the reCAPTCHA validation to them.
$("#recaptchaModal").dialog({
modal: true,
resizable: false,
width: 400,
open: function(e, ui){
Recaptcha.create("your_recaptcha_public_key",
"recaptcha_block",
{
theme: "red",
callback: Recaptcha.focus_resonse_field
}
);
},
close: function(e, ui){
Recaptcha.destroy();
},
buttons: {
"Validate reCAPTCHA": function(){
$.ajax({
url: "/url/to/verify/recaptcha",
type: 'post',
data: {
challenge: Recaptcha.get_challenge(),
response: Recaptcha.get_response(),
additional_data: here
},
success: function(data){
if(data != "success"){
alert("The reCAPTCHA wasn't entered correctly. Please try again.");
Recaptcha.reload();
}
else{
//-- submit your form via AJAX
}
}
});
},
Cancel: function(){
$(this).dialog("close");
}
}
});
As you can see, I am using an AJAX call to verify the reCAPTCHA challenge. reCAPTCHA does not support Javascript-only validation as a security measure. Therefore, any validation must be done on the server side.
reCAPTCHA offers several plugins for whichever server environment you prefer. Taking the PHP plugin as an example, all the server-side validation would take is the following:
include_once(‘/full/path/to/php/plugin’);
$CheckResponse = recaptcha_check_answer($YourPrivateKey, $_SERVER['REMOTE_ADDR'],$_POST[‘challenge’],$_POST[‘response’]);
if(!$CheckResponse->is_valid){
echo ‘success’;
}
else{
echo ‘fail’;
}
Easy, right? In this example, we are returning a success/fail string, but you can easily alter this to return JSON, XML, or whatever else you’d prefer.
Whichever flavor you choose, you can return the result of that validation to the Javascript. From here, you can handle the success or failure of the challenge, however you feel like. Personally, I like to use AJAX in this case. Here’s an example of what that would look like:
//-- submit your form via AJAX
$.ajax({
url: "/your/submission/url",
type: "post",
data: $('#'+formID).serialize(),
success: function(data){
//-- forward user to your thank you page
window.location.href = ‘/url/to/your/thank/you/page’;
}
});
Remember that formID variable we setup way back in the beginning? Here’s where the little guy comes into play. This variable will tell us which form on the page is being submitted, so long as the form element has an id attribute. You can use it to separate out submission and thank you page URLs in your successful reCAPTCHA block, or you can pass it as part of the URL (MVC-style), or even from the form element’s action attribute.
//-- submit your form via AJAX + switch statement
var submitURL = ‘’;
var thanksURL = ‘’;
switch(formID){
case ‘my_form’:
submitURL = “/submission/url/1”;
thanksURL = “/thankyou/url/1”;
break;
}
$.ajax({
url: submitURL,
type: "post",
data: $('#'+formID).serialize(),
success: function(data){
//-- forward user to your thank you page
window.location.href = thanksURL;
}
});
//-- submit your form via AJAX + MVC-style
$.ajax({
url: "/your/submission/url/"+formID,
type: "post",
data: $('#'+formID).serialize(),
success: function(data){
//-- forward user to your thank you page
window.location.href = ‘/url/to/your/thank/you/page/’+formID;
}
});
//-- submit your form via AJAX + action attribute example
var action = $('#'+formID).attr(“action”);
var thanks = $('#'+formID).attr(“rel”);
$.ajax({
url: action,
type: "post",
data: $('#'+formID).serialize(),
success: function(data){
//-- forward user to your thank you page
window.location.href = thanks;
}
});
Boom! By using the formID variable, we’ve successfully separated out form submissions, to their respective locations. And thanks to our original selector of $(‘form’), we now have this available on all forms on the page. In the end, we should have an HTML page that resembles something like this:
<html>
<head>
<title>reCAPTCHA</title>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.0/themes/base/jquery-ui.css">
<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.0.js"></script>
<script type="text/javascript" src="http://code.jquery.com/ui/1.10.0/jquery-ui.js"></script>
<script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>
</head>
<body>
<form id="my_form" name="my_form" method="post">
<input type="submit" value="Submit My Form" />
</form>
<form id="another_form" name="another_form" method="post">
<input type="submit" value="Submit Another Form" />
</form>
<div id="recaptchaModal" title="reCAPTCHA" style="display:none;">
<p>Please fill out this reCAPTCHA to submit this form.</p>
<div id="recaptcha_block"></div>
</div>
<script>
$('form').on('submit', function(e){
e.preventDefault();
var formID = e.currentTarget.id;
$("#recaptchaModal").dialog({
modal: true,
resizable: false,
width: 400,
open: function(e, ui){
Recaptcha.create("your_recaptcha_public_key",
"recaptcha_block",
{
theme: "red",
callback: Recaptcha.focus_resonse_field
}
);
},
close: function(e, ui){
Recaptcha.destroy();
},
buttons: {
"Validate reCAPTCHA": function(){
$.ajax({
url: "/url/to/verify/recaptcha",
type: 'post',
data: {
challenge: Recaptcha.get_challenge(),
response: Recaptcha.get_response(),
additional_data: here
},
success: function(data){
if(data != "success"){
alert("The reCAPTCHA wasn't entered correctly. Please try again.");
Recaptcha.reload();
}
else{
//-- submit your form with AJAX + MVC-style
$.ajax({
url: "/your/submission/url/"+formID,
type: "post",
data: $('#'+formID).serialize(),
success: function(data){
//-- forward user to your thank you page
window.location.href = '/url/to/your/thank/you/page/'+formID;
}
});
}
}
});
},
Cancel: function(){
$(this).dialog("close");
}
}
});
});
</script>
</body>
</html>
And there you have it! All forms are now secured with a reCAPTCHA. Hopefully you all enjoyed reading this, and that you learned something new and interesting today.
I know I learned something: Blog posts are hard!