Migrating From reCAPTCHA to hCaptcha: A Step-by-Step Switch Without Breaking Your Forms

Migrating From reCAPTCHA to hCaptcha: A Step-by-Step Switch Without Breaking Your Forms

Whether the trigger is reCAPTCHA's shrunken free tier, privacy and data-residency concerns, or a desire to stop sending every visitor to Google, teams switching to hCaptcha usually discover the same thing: the front-end swap is close to drop-in, and the server side is where the migration actually lives. Here is the exact path — client markup, the script change, the response-field rename, and the verification endpoint — plus the mistakes that quietly let bots back through during the cutover.

rCAPTCHA Team
rCAPTCHA Team
June 14, 2026 · 11 min read

hCaptcha was deliberately designed to be easy to adopt for sites already running reCAPTCHA v2 — its widget, attribute names, and verification flow intentionally mirror the shape of reCAPTCHA so that a migration is a rename rather than a rewrite. That similarity is a gift and a trap. The gift is that you can move a basic integration in an afternoon. The trap is that the similarity tempts teams to assume the pieces are interchangeable, leave one Google reference behind, and ship a form that looks migrated but verifies against the wrong service. This guide walks the swap one layer at a time so nothing is left half-converted.

Step 1: Register the Site and Get hCaptcha Keys

Try rCAPTCHA

Experience the technology discussed in this article.

Learn More →

Create an hCaptcha account and add your site to get a site key (public, used in the browser) and a secret key (private, used only on your server). This is the same two-key model reCAPTCHA uses, so the place those values live in your code does not change — only the values do. Keep the secret out of client-side code and out of your repository, exactly as you did for the reCAPTCHA secret. If your old keys were committed anywhere, rotate the mental habit too: treat the migration as the moment to move both secrets into environment variables if they were not already there.

Step 2: Swap the Client Script and Widget

Replace the reCAPTCHA script include with hCaptcha's, and change the widget container's class and data attribute. The before-and-after for a standard checkbox widget is small:

<!-- Before (reCAPTCHA v2) -->
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<div class="g-recaptcha" data-sitekey="RECAPTCHA_SITE_KEY"></div>

<!-- After (hCaptcha) -->
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
<div class="h-captcha" data-sitekey="HCAPTCHA_SITE_KEY"></div>

That is the heart of the front-end change: g-recaptcha becomes h-captcha, the script host changes, and the site key changes. If you used explicit rendering via JavaScript rather than the auto-rendered class, hCaptcha exposes an hcaptcha.render(), hcaptcha.getResponse(), and hcaptcha.reset() API that parallels reCAPTCHA's grecaptcha equivalents — the same multi-widget discipline applies, so if you render several on a page, the explicit-render and widget-ID rules we covered for reCAPTCHA carry over conceptually.

Step 3: Read the Renamed Response Field

On submit, the token your form posts changes name. Where reCAPTCHA put its token in g-recaptcha-response, hCaptcha puts it in h-captcha-response. Every place your backend reads the old field name must be updated, or the server will look for a token that no longer exists and treat every legitimate submission as missing one:

// Before
$token = $_POST['g-recaptcha-response'];
// After
$token = $_POST['h-captcha-response'];

This single rename is the most common migration miss. The widget renders, the user solves it, the front end looks perfect — and the backend silently rejects everyone because it is still reading g-recaptcha-response. It mirrors the broader class of token handoff failures where the client and server disagree about the field name.

Step 4: Change the Server-Side Verification Endpoint

This is the step people forget exists. reCAPTCHA verifies tokens against https://www.google.com/recaptcha/api/siteverify; hCaptcha verifies against https://api.hcaptcha.com/siteverify. The request shape is the same — a POST with secret and response parameters (and an optional remoteip) — and the JSON response again carries a top-level success boolean. Update the URL and the secret, keep the success-check logic, and re-test:

// hCaptcha server-side verification (PHP sketch)
$verify = file_get_contents(
  'https://api.hcaptcha.com/siteverify',
  false,
  stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => 'Content-Type: application/x-www-form-urlencoded',
    'content' => http_build_query([
      'secret'   => getenv('HCAPTCHA_SECRET'),
      'response' => $token,
      'remoteip' => $_SERVER['REMOTE_ADDR'] ?? null,
    ]),
  ]])
);
$result = json_decode($verify, true);
if (empty($result['success'])) {
    // reject: do not process the form
}

Because the success field is named the same, a sloppy migration that updates the field name but leaves the old Google verification URL in place will fail closed (Google rejects an hCaptcha token), which at least breaks loudly. The dangerous inverse is leaving verification disabled "temporarily" during the swap and forgetting to turn it back on — a form that renders a CAPTCHA but never checks the token is open to every bot that ignores the widget entirely.

Step 5: Test the Failure Paths, Not Just the Happy Path

  1. Solve and submit. Confirm a real solve produces a token and the server returns success: true and processes the form.
  2. Submit with no token. Strip the field with dev tools and confirm the server rejects it. If it accepts an empty token, your verification is not actually wired up.
  3. Submit a stale or reused token. hCaptcha tokens, like reCAPTCHA's, are single-use and time-limited; confirm a replayed token is rejected, the same lifecycle discipline behind timeout-or-duplicate failures.
  4. Remove every Google reference. Grep the codebase for g-recaptcha, grecaptcha, recaptcha/api.js, and the old siteverify URL. Any survivor is a half-migrated path.

Where rCAPTCHA Fits in a Migration Decision

Switching providers is a good moment to ask the larger question: do you want a visible image-puzzle widget at all? Both reCAPTCHA v2 and hCaptcha's interactive mode still put friction in front of real users, and motivated bots increasingly solve image challenges anyway. Behavioral verification like rCAPTCHA aims to reduce that visible friction by leaning on signals rather than puzzles, which is a different trade-off than a like-for-like provider swap. We are not pretending any single layer eliminates every bot, abuse, or accessibility problem — none does — but if you are already opening the integration to migrate, it is worth weighing a model change against a vendor change. Our CAPTCHA vs hCaptcha vs reCAPTCHA comparison lays the options side by side.

People Also Ask: reCAPTCHA to hCaptcha

Is hCaptcha a drop-in replacement for reCAPTCHA?

For a basic v2-style integration it is close: the widget class, script host, response-field name, and verification URL change, but the overall flow and two-key model are the same. Advanced or heavily customized reCAPTCHA setups need more care.

What replaces g-recaptcha-response in hCaptcha?

hCaptcha posts the token in h-captcha-response. Update every server-side read of g-recaptcha-response to the new field name.

What verification endpoint does hCaptcha use?

https://api.hcaptcha.com/siteverify, with secret and response POST parameters and a top-level success boolean in the JSON response — the same request shape as Google's siteverify, just a different host.

Do I have to remove all the Google reCAPTCHA code?

Yes. Leftover grecaptcha calls, the old script tag, or the old siteverify URL produce half-migrated paths that either fail loudly or, worse, skip verification. Grep the codebase and remove every reference.

Conclusion

Migrating from reCAPTCHA to hCaptcha is four renames and one new endpoint: new keys, the h-captcha widget class and script host, the h-captcha-response field, and the api.hcaptcha.com/siteverify URL. The work that actually protects you is the testing — submit with no token, submit a reused token, and grep out every surviving Google reference so no path quietly skips verification during the cutover. Do those, and the switch is uneventful. Skip them, and you ship a form that looks protected and isn't.

Sources & Further Reading

rCAPTCHA Blog
rCAPTCHA Blog

Insights on web security and bot detection

More from this blog →

Responses

No responses yet. Be the first to share your thoughts!