Using Google reCAPTCHA v3 in PHP

Google reCAPTCHA v3 is invisible—no challenges, no image grids. It watches how users interact with the page and returns a score between 0.0 (bot) and 1.0 (human). Your server gets this score and decides what to do: accept, reject, or ask for more proof.

No friction for legitimate users, but you need to tune score thresholds for your traffic, and Google's JavaScript loads on every page. If you want to avoid Google's data collection or need GDPR compliance, use the Cloudflare Turnstile guide instead.

Prerequisites

Front-End Setup

There's no visible widget. Load the API script with your site key and call grecaptcha.execute() before the form submits. The token goes into a hidden input.

<!-- 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) labels requests in your reCAPTCHA admin console so you can tell which form generated which scores. Use descriptive values: 'contact', 'login', '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. Check both success and score—a successful verification with a low score still means the request looks like a bot.

<?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 main tuning knob for v3. Google recommends 0.5, but the right value depends on your traffic.

During development, log the raw score, not just pass/fail. After a few hundred submissions you'll see where legitimate users cluster. VPN users, privacy browsers (Brave, Firefox with uBlock), and people with limited Google history score lower even when human—if that's your audience, lower the threshold or add a fallback challenge instead of blocking them.

cURL Alternative

If allow_url_fopen is disabled on your host, use cURL. The endpoint accepts POST 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 is surveillance infrastructure. Google's JavaScript loads on every page, and Google receives interaction signals from your users. Under GDPR, this requires privacy policy disclosure and likely cookie consent. If your users are in the EU or privacy matters to you, review the CAPTCHA alternatives comparison or switch to Cloudflare Turnstile, which does not feed data into an advertising network.

Further Reading