Using Google reCAPTCHA v3 in PHP

Google reCAPTCHA v3 is an invisible bot detection system — there are no challenges, no image grids, and no user interaction required. Instead, it monitors signals across the user's session and returns a score between 0.0 (almost certainly a bot) and 1.0 (almost certainly human). Your server receives this score and decides what to do: accept, reject, or trigger a secondary verification step.

Because it runs silently in the background, reCAPTCHA v3 introduces no friction for legitimate users. The trade-off is that you need to calibrate score thresholds for your traffic, and Google's JavaScript loads on every page that uses it. If GDPR compliance or avoiding Google data collection is a priority for your project, see the Cloudflare Turnstile guide instead.

Prerequisites

Front-End Setup

Unlike v2, reCAPTCHA v3 doesn't render a visible widget. You load the API script with your site key and call grecaptcha.execute() just before the form submits. The resulting token is written into a hidden input and submitted with the form.

<!-- Load the reCAPTCHA v3 API with your site key -->
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>

<form method="post" action="/contact" id="contact-form">
    <input type="text"  name="name"  placeholder="Your name"  />
    <input type="email" name="email" placeholder="Your email" />

    <!-- Hidden field to hold the reCAPTCHA token -->
    <input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response" />

    <button type="submit">Send</button>
</form>

<script>
document.getElementById('contact-form').addEventListener('submit', function (e) {
    e.preventDefault();
    const form = this;

    grecaptcha.ready(function () {
        grecaptcha.execute('YOUR_SITE_KEY', { action: 'submit' }).then(function (token) {
            document.getElementById('g-recaptcha-response').value = token;
            form.submit();
        });
    });
});
</script>

The action string ('submit' above) is a label that appears in your reCAPTCHA admin console, making it easier to distinguish scores from different forms on the same site. Use descriptive values like 'contact', 'login', or 'checkout'.

Server-Side Verification in PHP

Send the token to Google's siteverify endpoint via GET or POST. The response includes success, score, and action. Always check both success and score — a successful verification with a low score still indicates bot-like behaviour.

<?php
/**
 * Verify a reCAPTCHA v3 token.
 *
 * @param string $token     The g-recaptcha-response value
 * @param string $secretKey Your reCAPTCHA secret key
 * @param float  $threshold Minimum score to accept (0.0–1.0, default 0.5)
 * @return bool
 */
function verifyRecaptchaV3(string $token, string $secretKey, float $threshold = 0.5): bool
{
    if (empty($token)) {
        return false;
    }

    $url = 'https://www.google.com/recaptcha/api/siteverify?' . http_build_query([
        'secret'   => $secretKey,
        'response' => $token,
        'remoteip' => $_SERVER['REMOTE_ADDR'] ?? '',
    ]);

    $response = @file_get_contents($url);
    if ($response === false) {
        return false; // network error
    }

    $data = json_decode($response, true);

    return
        ($data['success'] ?? false) === true &&
        ($data['score']   ?? 0.0)   >= $threshold;
}

// Usage in your form processor:
$token = $_POST['g-recaptcha-response'] ?? '';

if (!verifyRecaptchaV3($token, 'YOUR_SECRET_KEY', 0.5)) {
    http_response_code(400);
    exit('Bot check failed or low confidence score.');
}

// Proceed with form handling

Understanding Score Thresholds

The $threshold parameter is the most important tuning knob in a v3 integration. Google recommends starting at 0.5, but the right value depends entirely on your traffic.

During development, log the raw score rather than just the pass/fail result. After observing a few hundred real submissions, you'll have a clear picture of where your legitimate users cluster and can set the threshold accordingly. Users on VPNs, privacy browsers (Brave, Firefox with uBlock), or with limited browsing history on Google properties tend to score lower even when human — if your audience skews toward these groups, lower the threshold or add a fallback challenge rather than blocking them outright.

cURL Alternative

If allow_url_fopen is disabled on your host, use cURL. The endpoint accepts a POST request as well as GET.

<?php
function verifyRecaptchaV3Curl(string $token, string $secretKey, float $threshold = 0.5): bool
{
    if (empty($token)) {
        return false;
    }

    $ch = curl_init('https://www.google.com/recaptcha/api/siteverify');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => http_build_query([
            'secret'   => $secretKey,
            'response' => $token,
            'remoteip' => $_SERVER['REMOTE_ADDR'] ?? '',
        ]),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 5,
    ]);

    $response = curl_exec($ch);
    curl_close($ch);

    if (!$response) {
        return false;
    }

    $data = json_decode($response, true);

    return
        ($data['success'] ?? false) === true &&
        ($data['score']   ?? 0.0)   >= $threshold;
}

Troubleshooting

Privacy Note

reCAPTCHA v3 loads Google's JavaScript on every page where it runs, and Google receives user interaction signals from those pages. Under GDPR and similar regulations, this typically requires disclosure in your privacy policy and, depending on interpretation, a cookie consent mechanism. If your users are in the EU and privacy compliance is a concern, review the CAPTCHA alternatives comparison or consider switching to Cloudflare Turnstile, which does not feed data into an advertising network.

Further Reading