natbox.io

CaptchaBox / Docs / PHP

Add a captcha to a PHP form

Last updated 6 November 2026 · By Mohammad AlSaraf

About 5 minutes. Plain PHP 7.4 or newer with cURL, no libraries.

This guide adds CaptchaBox to a PHP contact form. The visitor's browser solves a small puzzle in the background; your PHP code asks CaptchaBox whether the answer is valid before it sends the message. No image puzzles, no cookies.

1. Get your keys

Sign in to the CaptchaBox client area (a free account is enough) and add your site with its domain. You get a site key for the page and a secret for your server. Keep the secret out of your HTML and out of git: put it in an environment variable.

2. Put the widget in your form

Load the script once, and put the widget anywhere inside the <form>:

<script src="https://captcha.natbox.io/v1/widget.js" async defer></script>

<form method="post">
  ...your fields...
  <div class="captchabox" data-sitekey="YOUR_SITE_KEY"></div>
  <button>Send</button>
</form>

The widget adds a hidden field, captchabox-response, with a one-time token.

3. Check the token in PHP

This function sends the token and your secret to CaptchaBox. Call it before you do anything with the form:

function captchabox_verify(string $secret, string $token): bool
{
    if ($token === '') {
        return false;
    }
    $ch = curl_init('https://captcha.natbox.io/v1/siteverify');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query(['secret' => $secret, 'response' => $token]),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
    ]);
    $body = curl_exec($ch);
    curl_close($ch);
    $r = json_decode((string) $body, true);
    return !empty($r['success']);
}
if (!captchabox_verify(getenv('CAPTCHABOX_SECRET'), $_POST['captchabox-response'] ?? '')) {
    http_response_code(400);
    exit('The captcha check failed. Please try again.');
}

No cURL? file_get_contents with a stream context works too:

$r = json_decode(file_get_contents('https://captcha.natbox.io/v1/siteverify', false, stream_context_create(['http' => [
    'method' => 'POST', 'header' => 'Content-Type: application/x-www-form-urlencoded', 'timeout' => 10,
    'content' => http_build_query(['secret' => $secret, 'response' => $_POST['captchabox-response'] ?? '']),
]])), true);
$ok = !empty($r['success']);

The complete example

One file you can run with php -S localhost:8000 contact.php:

<?php
// CaptchaBox example: a plain PHP contact form (one file).
// Run: CAPTCHABOX_SITE_KEY=... CAPTCHABOX_SECRET=... php -S 0.0.0.0:8000 contact.php
// Test keys that always pass, on any domain: cb_test_pass / cbs_test_pass

$siteKey = getenv('CAPTCHABOX_SITE_KEY') ?: 'cb_test_pass';
$secret  = getenv('CAPTCHABOX_SECRET') ?: 'cbs_test_pass';

/** Asks CaptchaBox whether the token from the form is valid. Each token works once, for 5 minutes. */
function captchabox_verify(string $secret, string $token): bool
{
    if ($token === '') {
        return false;
    }
    $ch = curl_init('https://captcha.natbox.io/v1/siteverify');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query(['secret' => $secret, 'response' => $token]),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
    ]);
    $body = curl_exec($ch);
    curl_close($ch);
    $r = json_decode((string) $body, true);
    return !empty($r['success']);
}

$message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!captchabox_verify($secret, $_POST['captchabox-response'] ?? '')) {
        http_response_code(400);
        $message = 'The captcha check failed. Please try again.';
    } else {
        // The visitor passed: send the email, save the message, etc.
        $message = 'Thanks, your message was sent.';
    }
}
?>
<!doctype html>
<meta charset="utf-8">
<title>Contact</title>
<script src="https://captcha.natbox.io/v1/widget.js" async defer></script>

<?php if ($message): ?><p id="result"><?= htmlspecialchars($message) ?></p><?php endif ?>
<form method="post">
  <input name="email" type="email" placeholder="Your email" required>
  <textarea name="message" placeholder="Message" required></textarea>
  <div class="captchabox" data-sitekey="<?= htmlspecialchars($siteKey) ?>"></div>
  <button>Send</button>
</form>

Trying it out? The test keys cb_test_pass (site key) and cbs_test_pass (secret) always pass, on any domain including localhost. Swap in your own keys from the client area before going live.

PHP captcha questions

Why check on the server? The widget already says "verified".

Bots do not run your page; they post straight to your PHP script, and can put anything in the hidden field. Only the server check proves the token is real, unused and made for your site.

What if CaptchaBox cannot be reached?

The example refuses the form, which is the safe choice. For a login form you may prefer to let people in when the check cannot be made at all (a cURL error, not a failed check), so an outage never locks anyone out.

Does it work with forms sent by JavaScript?

Yes. Get a token with await captchabox.execute(), send it with your data, and call captchabox.reset() afterwards. See the JavaScript API.

Other platforms: all CaptchaBox docs. Questions? Open a ticket, we reply within 24 hours.