101

Until now, I was working with Google Recaptcha v2, but now I want to update my WebApp using the lastest version (v3).

Is it possible to anyone add a fully working Google Recaptcha v3 example for a basic form as I can't find any working demos of it?

I'd really appreciate it.

Thank you very much.

PS: I'm using Java Servlets on the server side, but it doesn't matter if you explain using PHP or whatever.

6
  • 2
    Here is the link: recaptcha-demo.appspot.com Just request the score for v3 and it will give a response back in JSON
    – Suleman
    Sep 11, 2018 at 13:43
  • 11
    I have created a demo but this in PHP Visit my blog link
    – PHP Kishan
    Nov 3, 2018 at 4:44
  • but how can i put in div ? Nov 9, 2018 at 4:14
  • @FreddySidauruk You don't put in a div, it is executed via javascript function that calls google api which will then result you a response just like recaptchav2.
    – Suleman
    Nov 9, 2018 at 11:30
  • 1
    I posted a simple but detailed demo in pure JS and PHP here: stackoverflow.com/questions/50405977/…
    – wkille
    Jul 25, 2019 at 12:54

8 Answers 8

143

Simple code to implement ReCaptcha v3

The basic JS code

<script src="https://www.google.com/recaptcha/api.js?render=your reCAPTCHA site key here"></script>
<script>
    grecaptcha.ready(function() {
    // do request for recaptcha token
    // response is promise with passed token
        grecaptcha.execute('your reCAPTCHA site key here', {action:'validate_captcha'})
                  .then(function(token) {
            // add token value to form
            document.getElementById('g-recaptcha-response').value = token;
        });
    });
</script>

The basic HTML code

<form id="form_id" method="post" action="your_action.php">
    <input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response">
    <input type="hidden" name="action" value="validate_captcha">
    .... your fields
</form>

The basic PHP code

if (isset($_POST['g-recaptcha-response'])) {
    $captcha = $_POST['g-recaptcha-response'];
} else {
    $captcha = false;
}

if (!$captcha) {
    //Do something with error
} else {
    $secret   = 'Your secret key here';
    $response = file_get_contents(
        "https://www.google.com/recaptcha/api/siteverify?secret=" . $secret . "&response=" . $captcha . "&remoteip=" . $_SERVER['REMOTE_ADDR']
    );
    // use json_decode to extract json response
    $response = json_decode($response);

    if ($response->success === false) {
        //Do something with error
    }
}

//... The Captcha is valid you can continue with the rest of your code
//... Add code to filter access using $response . score
if ($response->success==true && $response->score <= 0.5) {
    //Do something to denied access
}

You have to filter access using the value of $response.score. It can takes values from 0.0 to 1.0, where 1.0 means the best user interaction with your site and 0.0 the worst interaction (like a bot). You can see some examples of use in ReCaptcha documentation.

12
  • 5
    The code you've posted doesn't check the value of the score field; if I understand the docs correctly, success only indicates whether the posted request was valid; the actual information about the interaction (i.e. legitimate or not) is contained in the score field.
    – user4520
    Sep 12, 2019 at 19:34
  • 19
    Its in the docs: Note: reCAPTCHA tokens expire after two minutes. If you're protecting an action with reCAPTCHA, make sure to call execute when the user takes the action. However, you call execute once the library is loaded. I would fix that.
    – Adam
    Feb 4, 2020 at 22:59
  • 6
    One has to wonder, why they require the developer to pass their key twice.
    – cdosborn
    Mar 17, 2020 at 14:58
  • 18
    I wonder how a BAD example like this contains so many up-votes. Sep 1, 2020 at 19:00
  • 3
    After having no success with several answers (including this one), I had more luck with this answer.
    – ashleedawg
    Nov 27, 2020 at 12:18
31

I thought a fully-functioning reCaptcha v3 example demo in PHP, using a Bootstrap 4 form, might be useful to some.

Reference the shown dependencies, swap in your email address and keys (create your own keys here), and the form is ready to test and use. I made code comments to better clarify the logic and also included commented-out console log and print_r lines to quickly enable viewing the validation token and data generated from Google.

The included jQuery function is optional, though it does create a much better user prompt experience in this demo.


PHP file (mail.php):

Add secret key (2 places) and email address where noted.

<?php

if ($_SERVER["REQUEST_METHOD"] == "POST") {

  # BEGIN Setting reCaptcha v3 validation data
  $url = "https://www.google.com/recaptcha/api/siteverify";
  $data = [
    'secret' => "your-secret-key-here",
    'response' => $_POST['token'],
    'remoteip' => $_SERVER['REMOTE_ADDR']
  ];

  $options = array(
    'http' => array(
      'header'  => "Content-type: application/x-www-form-urlencoded\r\n",
      'method'  => 'POST',
      'content' => http_build_query($data)
    )
    );
  
  # Creates and returns stream context with options supplied in options preset 
  $context  = stream_context_create($options);
  # file_get_contents() is the preferred way to read the contents of a file into a string
  $response = file_get_contents($url, false, $context);
  # Takes a JSON encoded string and converts it into a PHP variable
  $res = json_decode($response, true);
  # END setting reCaptcha v3 validation data
   
    // print_r($response); 
# Post form OR output alert and bypass post if false. NOTE: score conditional is optional
# since the successful score default is set at >= 0.5 by Google. Some developers want to
# be able to control score result conditions, so I included that in this example.

  if ($res['success'] == true && $res['score'] >= 0.5) {
 
    # Recipient email
    $mail_to = "[email protected]";
    
    # Sender form data
    $subject = trim($_POST["subject"]);
    $name = str_replace(array("\r","\n"),array(" "," ") , strip_tags(trim($_POST["name"])));
    $email = filter_var(trim($_POST["email"]), FILTER_SANITIZE_EMAIL);
    $phone = trim($_POST["phone"]);
    $message = trim($_POST["message"]);
    
    if (empty($name) OR !filter_var($email, FILTER_VALIDATE_EMAIL) OR empty($phone) OR empty($subject) OR empty($message)) {
      # Set a 400 (bad request) response code and exit
      http_response_code(400);
      echo '<p class="alert-warning">Please complete the form and try again.</p>';
      exit;
    }

    # Mail content
    $content = "Name: $name\n";
    $content .= "Email: $email\n\n";
    $content .= "Phone: $phone\n";
    $content .= "Message:\n$message\n";

    # Email headers
    $headers = "From: $name <$email>";

    # Send the email
    $success = mail($mail_to, $subject, $content, $headers);
    
    if ($success) {
      # Set a 200 (okay) response code
      http_response_code(200);
      echo '<p class="alert alert-success">Thank You! Your message has been successfully sent.</p>';
    } else {
      # Set a 500 (internal server error) response code
      http_response_code(500);
      echo '<p class="alert alert-warning">Something went wrong, your message could not be sent.</p>';
    }   

  } else {

    echo '<div class="alert alert-danger">
        Error! The security token has expired or you are a bot.
       </div>';
  }  

} else {
  # Not a POST request, set a 403 (forbidden) response code
  http_response_code(403);
  echo '<p class="alert-warning">There was a problem with your submission, please try again.</p>';
} ?>

HTML <head>

Bootstrap CSS dependency and reCaptcha client-side validation Place between <head> tags - paste your own site-key where noted.

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<script src="https://www.google.com/recaptcha/api.js?render=your-site-key-here"></script>

HTML <body>

Place between <body> tags.

<!-- contact form demo container -->
<section style="margin: 50px 20px;">
  <div style="max-width: 768px; margin: auto;">
    
    <!-- contact form -->
    <div class="card">
      <h2 class="card-header">Contact Form</h2>
      <div class="card-body">
        <form class="contact_form" method="post" action="mail.php">

          <!-- form fields -->
          <div class="row">
            <div class="col-md-6 form-group">
              <input name="name" type="text" class="form-control" placeholder="Name" required>
            </div>
            <div class="col-md-6 form-group">
              <input name="email" type="email" class="form-control" placeholder="Email" required>
            </div>
            <div class="col-md-6 form-group">
              <input name="phone" type="text" class="form-control" placeholder="Phone" required>
            </div>
            <div class="col-md-6 form-group">
              <input name="subject" type="text" class="form-control" placeholder="Subject" required>
            </div>
            <div class="col-12 form-group">
              <textarea name="message" class="form-control" rows="5" placeholder="Message" required></textarea>
            </div>

            <!-- form message prompt -->
            <div class="row">
              <div class="col-12">
                <div class="contact_msg" style="display: none">
                  <p>Your message was sent.</p>
                </div>
              </div>
            </div>

            <div class="col-12">
              <input type="submit" value="Submit Form" class="btn btn-success" name="post">
            </div>

            <!-- hidden reCaptcha token input -->
            <input type="hidden" id="token" name="token">
          </div>

        </form>
      </div>
    </div>

  </div>
</section>
<script>
  grecaptcha.ready(function() {
    grecaptcha.execute('your-site-key-here', {action: 'homepage'}).then(function(token) {
       // console.log(token);
       document.getElementById("token").value = token;
    });
    // refresh token every minute to prevent expiration
    setInterval(function(){
      grecaptcha.execute('your-site-key-here', {action: 'homepage'}).then(function(token) {
        console.log( 'refreshed token:', token );
        document.getElementById("token").value = token;
      });
    }, 60000);

  });
</script>

<!-- References for the optional jQuery function to enhance end-user prompts -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="form.js"></script>

Optional jQuery function for enhanced UX (form.js):

(function ($) {
'use strict';

var form = $('.contact_form'),
  message = $('.contact_msg'),
  form_data;

// Success function
function done_func(response) {
  message.fadeIn()
  message.html(response);
  setTimeout(function () {
    message.fadeOut();
  }, 10000);
  form.find('input:not([type="submit"]), textarea').val('');
}

// fail function
function fail_func(data) {
  message.fadeIn()
  message.html(data.responseText);
  setTimeout(function () {
    message.fadeOut();
  }, 10000);
}

form.submit(function (e) {
  e.preventDefault();
  form_data = $(this).serialize();
  $.ajax({
    type: 'POST',
    url: form.attr('action'),
    data: form_data
  })
  .done(done_func)
  .fail(fail_func);
}); })(jQuery);
16
  • 3
    very thorough; I got this one quickly working after giving up on the other (higher scored) answers. Thanks!
    – ashleedawg
    Nov 27, 2020 at 12:16
  • 2
    Note: "reCAPTCHA tokens expire after two minutes. If you're protecting an action with reCAPTCHA, make sure to call execute when the user takes the action rather than on page load."(src) This is especially important on a comment form -- I've had that happen as a user on live sites, after typing out detailed (lengthy) angry feedback, and then the site's form doesn't let me submit it. It can be very frustrating!
    – ashleedawg
    Nov 27, 2020 at 12:55
  • 2
    @ashleedawg edited to include refreshing token every 60 seconds Feb 8, 2021 at 17:58
  • @TalkNerdyToMe excuse my ignorance, in what part of your code are you refreshing the token every 60 seconds?
    – Albert
    Feb 23, 2021 at 20:48
  • 1
    You can get your SITE key and SECRET key here: google.com/recaptcha/admin/create
    – timgavin
    Apr 19, 2021 at 14:08
5

I am assuming you have site key and secret in place. Follow this step.

In your HTML file, add the script.

 <script src="https://www.google.com/recaptcha/api.js?render=put your site key here"></script>

Also, do use jQuery for easy event handling.

Here is the simple form.

 <form id="comment_form" action="form.php" method="post" >
      <input type="email" name="email" placeholder="Type your email" size="40"><br><br>
      <textarea name="comment" rows="8" cols="39"></textarea><br><br>
      <input type="submit" name="submit" value="Post comment"><br><br>
    </form>

You need to initialize the Google recaptcha and listen for the ready event. Here is how to do that.

     <script>
       // when form is submit
    $('#comment_form').submit(function() {
        // we stoped it
        event.preventDefault();
        var email = $('#email').val();
        var comment = $("#comment").val();
        // needs for recaptacha ready
        grecaptcha.ready(function() {
            // do request for recaptcha token
            // response is promise with passed token
            grecaptcha.execute('put your site key here', {action: 'create_comment'}).then(function(token) {
                // add token to form
                $('#comment_form').prepend('<input type="hidden" name="g-recaptcha-response" value="' + token + '">');
                $.post("form.php",{email: email, comment: comment, token: token}, function(result) {
                        console.log(result);
                        if(result.success) {
                                alert('Thanks for posting comment.')
                        } else {
                                alert('You are spammer ! Get the @$%K out.')
                        }
                });
            });
        });
  });
  </script>

Here is the sample PHP file. You can use Servlet or Node or any backend language in place of it.

<?php

        $email;$comment;$captcha;
        if(isset($_POST['email'])){
          $email=$_POST['email'];
        }if(isset($_POST['comment'])){
          $comment=$_POST['comment'];
        }if(isset($_POST['token'])){
          $captcha=$_POST['token'];
          }
        if(!$captcha){
          echo '<h2>Please check the the captcha form.</h2>';
          exit;
        }
        $secretKey = "put your secret key here";
        $ip = $_SERVER['REMOTE_ADDR'];

        // post request to server

        $url =  'https://www.google.com/recaptcha/api/siteverify?secret=' . urlencode($secretKey) .  '&response=' . urlencode($captcha);
        $response = file_get_contents($url);
        $responseKeys = json_decode($response,true);
        header('Content-type: application/json');
        if($responseKeys["success"]) {
                echo json_encode(array('success' => 'true'));
        } else {
                echo json_encode(array('success' => 'false'));
        }
?>

Here is the tutorial link: https://codeforgeek.com/2019/02/google-recaptcha-v3-tutorial/

Hope it helps.

2
  • 8
    This is wrong and doesn't take into account the score which is needed in v3. Don't follow this guide, read in comments on the page linked.
    – tony
    Apr 9, 2020 at 7:59
  • After having no success with several answers (including this one), I had more luck with this answer.
    – ashleedawg
    Nov 27, 2020 at 12:18
3

We use recaptcha-V3 only to see site traffic quality, and used it as non blocking. Since recaptcha-V3 doesn't require to show on site and can be used as hidden but you have to show recaptcha privacy etc links (as recommended)

Script Tag in Head

<script src="https://www.google.com/recaptcha/api.js?onload=ReCaptchaCallbackV3&render='SITE KEY' async defer></script>

Note: "async defer" make sure its non blocking which is our specific requirement

JS Code:

<script>
    ReCaptchaCallbackV3 = function() {
        grecaptcha.ready(function() {
            grecaptcha.execute("SITE KEY").then(function(token) {
                $.ajax({
                    type: "POST",
                    url: `https://api.${window.appInfo.siteDomain}/v1/recaptcha/score`,
                    data: {
                        "token" : token,
                    },
                    success: function(data) {
                        if(data.response.success) {
                            window.recaptchaScore = data.response.score;
                            console.log('user score ' + data.response.score)
                        }
                    },
                    error: function() {
                        console.log('error while getting google recaptcha score!')
                    }
                });

            });
        });
    };
</script> 

HTML/Css Code:

there is no html code since our requirement is just to get score and don't want to show recaptcha badge.

Backend - Laravel Code:

Route:

Route::post('/recaptcha/score', 'Api\\ReCaptcha\\RecaptchaScore@index');


Class:

class RecaptchaScore extends Controller
{
    public function index(Request $request)
    {
        $score = null;

        $response = (new Client())->request('post', 'https://www.google.com/recaptcha/api/siteverify', [
            'form_params' => [
                'response' => $request->get('token'),
                'secret' => 'SECRET HERE',
            ],
        ]);

        $score = json_decode($response->getBody()->getContents(), true);

        if (!$score['success']) {
            Log::warning('Google ReCaptcha Score', [
                'class' => __CLASS__,
                'message' => json_encode($score['error-codes']),
            ]);
        }

        return [
            'response' => $score,
        ];
    }
} 

we get back score and save in variable which we later user when submit form.

Reference: https://developers.google.com/recaptcha/docs/v3 https://developers.google.com/recaptcha/

2
  • After having no success with several answers (including this one), I had more luck with this answer.
    – ashleedawg
    Nov 27, 2020 at 12:18
  • @ashleedawg sorry if that didn't work for you ! i've just tested again and look like all good ! your reference is simple php implementation if you use this colutin which i've mentioned is written for #Laravel but it should also work if you simply use RecaptchaScore class. Nov 28, 2020 at 17:15
3

I have seen most of the articles that don't work properly that's why new developers and professional developers get confused about it.

I am explaining to you in a very simple way. In this code, I am generating a google Recaptcha token at the client side at every 3 seconds of time interval because the token is valid for only a few minutes that's why if any user takes time to fill the form then it may be expired.

First I have an index.php file where I am going to write HTML and JavaScript code.

    <!DOCTYPE html>
<html>
   <head>
      <title>Google Recaptcha V3</title>
   </head>
   <body>
      <h1>Google Recaptcha V3</h1>
      <form action="recaptcha.php" method="post">
         <label>Name</label>
         <input type="text" name="name" id="name">
         <input type="hidden" name="token" id="token" /> 
         <input type="hidden" name="action" id="action" /> 
         <input type="submit" name="submit">
      </form>
      <script src="https://www.google.com/recaptcha/api.js?render=put your site key here"></script>
      <script  src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
      <script type="text/javascript">
         $(document).ready(function(){
            setInterval(function(){
            grecaptcha.ready(function() {
                grecaptcha.execute('put your site key here', {action: 'application_form'}).then(function(token) {
                    $('#token').val(token);
                    $('#action').val('application_form');
                });
            });
            }, 3000);
         });

      </script>
   </body>
</html>

Next, I have created recaptcha.php file to execute it at the server side

<?php

if ($_POST['submit']) {
    $name   = $_POST['name'];
    $token  = $_POST['token'];
    $action = $_POST['action'];

    $curlData = array(
        'secret' => 'put your secret key here',
        'response' => $token
    );

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify");
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($curlData));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $curlResponse = curl_exec($ch);

    $captchaResponse = json_decode($curlResponse, true);

    if ($captchaResponse['success'] == '1' && $captchaResponse['action'] == $action && $captchaResponse['score'] >= 0.5 && $captchaResponse['hostname'] == $_SERVER['SERVER_NAME']) {
        echo 'Form Submitted Successfully';
    } else {
        echo 'You are not a human';
    }
}

Source of this code. If you would like to know the explanation of this code please visit. Google reCAPTCHA V3 integration in PHP

6
  • I agree with your first sentence... and this didn't work for me either. ("You are not a human"). The only answer that worked for me was this.
    – ashleedawg
    Nov 27, 2020 at 12:26
  • Hi @clayray, I have already applied score in code. Dec 6, 2020 at 12:34
  • Ah yes, so you have @SumitKumarGupta. Apologies, I will delete my comment.
    – clayRay
    Dec 7, 2020 at 0:09
  • 1
    this worked for me. there are two places for site key and one place for secret. dont miss that out guys
    – Chris
    Jun 7, 2021 at 6:46
  • I implemented V3 and generated the recaptcha token on post, when they click the, in this case, "Submit" button. No need to create new key every [interval of time].
    – Kershaw
    Aug 22, 2022 at 20:01
2

For a "basic form" (as the original question asks) what's needed is simple if you're content to validate on the server. Here's a complete HTML page:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://www.google.com/recaptcha/api.js"></script>
<script>
<!--
function onSubmit() {
    var form = document.forms[0];
    if ( form['name'].value ) {
        form.submit();
    } else {
        alert( 'Please provide a name.' );
    }
}
//-->
</script> 
</head>
<body>
    <form action="process.asp" method="post">
        Name: <input type="text" name="name" /><br /><br />
        <button class="g-recaptcha" data-sitekey="SITE_KEY" data-callback='onSubmit' data-action='contact'>Send</button>
    </form>
</body>
</html>

And here's the complete page for processing it, using Classic ASP (filename = process.asp) for simplicity:

<%@ Language=JavaScript %>
<%
var name = Request( 'name' ).Item;
var recaptchaResponse = Request( 'g-recaptcha-response' ).Item;
var ip = Request.ServerVariables( 'REMOTE_ADDR' );
var xmlhttp = Server.CreateObject( 'MSXML2.ServerXMLHTTP' );
var query = 'secret=SECRET_KEY&response=' + recaptchaResponse + '&remoteip=' + ip;
xmlhttp.open( 'POST', 'https://www.google.com/recaptcha/api/siteverify?' + query, false ); // false says to wait for response
xmlhttp.send();
var response = JSON.parse( xmlhttp.responseText );
Response.Write( name + ' is a ' + (response.success && response.action == 'contact' && response.score > 0.5 ? 'HUMAN' : 'ROBOT') );
%>

A few notes:

  1. You'll supply your own SITE_KEY and SECRET_KEY.
  2. You'll need a JSON parser.
  3. You'll do the server-side POST using a method suitable for your server.
  4. I added one simple form field validation so you can see how to integrate that.
  5. You can make the "action" string anything you want, but be sure that what's on the server is consistent with what's in the HTML.
  6. You might want to respond differently to a response.success that isn't true or a response.action that doesn't match your action string, or do other error checking.
  7. You might want a score conditional other than "> 0.5".
  8. This code has no problems with the two-minute timeout.
2

I process POST on PHP from an angular ajax call. I also like to see the SCORE from google.

This works well for me...

$postData = json_decode(file_get_contents('php://input'), true); //get data sent via post
$captcha = $postData['g-recaptcha-response'];

header('Content-Type: application/json');
if($captcha === ''){
    //Do something with error
    echo '{ "status" : "bad", "score" : "none"}';
} else {
    $secret   = 'your-secret-key';
    $response = file_get_contents(
        "https://www.google.com/recaptcha/api/siteverify?secret=" . $secret . "&response=" . $captcha . "&remoteip=" . $_SERVER['REMOTE_ADDR']
    );
    // use json_decode to extract json response
    $response = json_decode($response);

    if ($response->success === false) {
        //Do something with error
        echo '{ "status" : "bad", "score" : "none"}';
    }else if ($response->success==true && $response->score <= 0.5) {
        echo '{ "status" : "bad", "score" : "'.$response->score.'"}';
    }else {
        echo '{ "status" : "ok", "score" : "'.$response->score.'"}';
    }
}

On HTML

<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response">

On js

$scope.grabCaptchaV3=function(){
     var myCaptcha = angular.element('#g-recaptcha-response').val();         
     var params = {
                     method: 'POST',
                     url: 'api/recaptcha.php',
                     headers: {
                       'Content-Type': undefined
                     },
                     data:   {'g-recaptcha-response' : myCaptcha }
     }
     $http(params).then(function(result){ 
                console.log(result.data);
     }, function(response){
                console.log(response.statusText);
     }); 
}
0

if you are newly implementing recaptcha on your site, I would suggest adding api.js and let google collect behavioral data of your users 1-2 days. It is much fail-safe this way, especially before starting to use score.

1
  • Welcome to SO! More info or links would be helpful. (Check out How to Answer.)
    – ashleedawg
    Nov 27, 2020 at 12:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.