Diagnose PHP reCAPTCHA Verification Timeouts over IPv6

When reCAPTCHA verification hangs but succeeds after forcing IPv4, the likely difference is the server’s outbound IPv6 path, not the PHP upgrade itself. Use PHP cURL so you can bound the connection, inspect the failure, and compare address families without weakening TLS verification.

Last updated: October 23, 2026.

<?php
$curl = curl_init('https://www.google.com/recaptcha/api/siteverify');

curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'secret' => $_ENV['RECAPTCHA_SECRET'],
        'response' => $token,
    ]),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 5,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_IPRESOLVE => CURL_IPRESOLVE_WHATEVER,
]);

$body = curl_exec($curl);
if ($body === false) {
    throw new RuntimeException(curl_error($curl), curl_errno($curl));
}

$result = json_decode($body, true, flags: JSON_THROW_ON_ERROR);

For one controlled comparison, change CURL_IPRESOLVE_WHATEVER to CURL_IPRESOLVE_V4. If only IPv4 works, keep verification fail-closed and investigate DNS, routing, and firewall behavior on the web server.

Separate application errors from network errors

Google requires a server-side POST to the documented siteverify endpoint. A response containing an error code is an application-level result. A connect timeout means PHP did not receive that response, so changing the secret or token cannot repair the transport path.

Run DNS and HTTPS tests from the same host and runtime environment as PHP. Compare IPv4 and IPv6 resolution, then check the default route, outbound firewall policy, proxy settings, and hosting-provider IPv6 support. Record curl_errno(), curl_error(), and curl_getinfo() in a protected log. Never log the secret or complete user token.

Treat forced IPv4 as evidence, not the root fix

The official libcurl IP resolution option confirms that CURL_IPRESOLVE_V4 restricts a transfer to IPv4. It can be a practical temporary workaround, but it also hides a broken IPv6 route that may affect other APIs. The PHP cURL documentation provides connection and total timeout options so failures remain bounded.

Do not disable certificate or hostname verification. On a verification timeout, reject or retry the form with a fresh challenge; reCAPTCHA response tokens expire quickly and are single-use. Continue with handling PHP cURL option failures, diagnosing cURL error 28, and adding CAPTCHA protection to a PHP form.

Related Web Cheat Sheet guides

Sergey Kornilov

Sergey Kornilov