# ambethia/recaptcha: integrating Google reCAPTCHA v2 and v3 into Ruby apps

> The recaptcha gem gives Rails, Sinatra, and Rack apps two helper methods, recaptcha_tags in views and verify_recaptcha in controllers, to wire up Google reCAPTCHA without writing the API calls yourself. It supports v2 Checkbox, v2 Invisible, and v3, but the key type registered in Google's admin console must match the method you call or the widget returns an error.

**ambethia/recaptcha** — ReCaptcha helpers for ruby apps

- Repository: https://github.com/ambethia/recaptcha
- Website: http://github.com/ambethia/recaptcha
- Stars: 2,006 · Forks: 437
- Language: Ruby
- License: MIT
- Published: 2026-10-09 · Updated: 2026-10-09 · Language: en
- Canonical page: https://hysenlabs.com/projects/ambethia-recaptcha

## recaptcha_tags writes the widget; verify_recaptcha calls Google's verify endpoint

Integration requires 2 things: a view helper that outputs the reCAPTCHA widget JavaScript and a controller check that asks Google whether the submitted token is valid. The gem provides both as named helpers rather than routes you configure separately.

Adding recaptcha_tags inside a form outputs the script tag and the widget markup that Google requires. In the controller, verify_recaptcha checks the g-recaptcha-response parameter by calling Google's verify API and returns true on a valid response or false on failure. The README's controller example shows the pattern:

```ruby
# app/controllers/users_controller.rb
@user = User.new(params[:user].permit(:name))
if verify_recaptcha(model: @user) && @user.save
  redirect_to @user
else
  render 'new'
end
```

Passing model: @user to verify_recaptcha adds a validation error to that model object when the CAPTCHA check fails. Rendering the form again then shows the error alongside any other field errors, which fits Rails' standard error display without a separate code path for CAPTCHA failures. A second method, verify_recaptcha!, raises an exception on failure instead of returning false, which suits controller actions where re-rendering is not an option.

## gem 'recaptcha' in Gemfile, then two environment variables to point at your key

Rails installation uses the gem name recaptcha without any extras.

```ruby
gem "recaptcha"
```

API keys come from 2 environment variables.

```shell
export RECAPTCHA_SITE_KEY   = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
export RECAPTCHA_SECRET_KEY = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
```

SITE_KEY is the public key embedded in the page. SECRET_KEY is the private key used for server-side verification. Keeping both out of version control means environment variables or Rails secrets, and the README mentions the dotenv gem for development, noting that it must appear above gem 'recaptcha' in the Gemfile if used.

For development with localhost:3000, Google's admin console requires entering localhost or 127.0.0.1 as an allowed domain, otherwise the widget will refuse to render.

For Sinatra or Rack apps, auto-inclusion does not happen. The gem still goes in the Gemfile and the same environment variables apply, but 2 module includes must be done manually: Recaptcha::Adapters::ViewMethods in whichever class renders views, and Recaptcha::Adapters::ControllerMethods in whichever handler class calls verify_recaptcha. A sinatra demo application lives under demo/sinatra in the repository and covers the full setup.

## 3 key types, 3 methods, and no compatibility between them

Google issues 3 types of reCAPTCHA key from its admin console: v3, v2 Checkbox, and v2 Invisible. Each maps to a specific gem method, and those mappings are enforced by Google's API at verification time rather than at the gem's configuration stage. A misconfigured key fails when a user actually submits a form, not when the server starts.

v2 Checkbox uses recaptcha_tags to render the checkbox widget. v2 Invisible uses invisible_recaptcha_tags to validate in the background without any visible UI. v3 uses recaptcha_v3 to assign a risk score to each request rather than interrupting users with a challenge. The README table of contents also lists recaptcha_reply as a v3-related method, but the document is truncated before that section's content appears.

Using a v2 method with a v3 key, or using recaptcha_tags with a v2 Invisible key, produces errors from Google rather than from the gem itself. The README gives 2 specific error strings: "Invalid key type" and "This site key is not enabled for the invisible captcha." Both mean the key type registered at Google and the method called in the view do not match.

Changing the key type later requires updating the key registration at Google's console and updating every view and controller that calls the old method.

## Enterprise API keys need 3 more environment variables and still require RECAPTCHA_SITE_KEY

The standard API uses RECAPTCHA_SITE_KEY and RECAPTCHA_SECRET_KEY. The Enterprise API replaces that pair with 3 variables: RECAPTCHA_ENTERPRISE, RECAPTCHA_ENTERPRISE_API_KEY, and RECAPTCHA_ENTERPRISE_PROJECT_ID.

```shell
export RECAPTCHA_ENTERPRISE            = 'true'
export RECAPTCHA_ENTERPRISE_API_KEY    = 'AIzvFyE3TU-g4K_Kozr9F1smEzZSGBVOfLKyupA'
export RECAPTCHA_ENTERPRISE_PROJECT_ID = 'my-project'
```

RECAPTCHA_ENTERPRISE_API_KEY is a Google Cloud Project API key, generated from https://console.cloud.google.com/apis/credentials rather than from the reCAPTCHA admin console. RECAPTCHA_ENTERPRISE_PROJECT_ID identifies the Google Cloud project.

One variable stays the same for enterprise: RECAPTCHA_SITE_KEY must still be set, and it holds the enterprise reCAPTCHA key id rather than a standard key. RECAPTCHA_SECRET_KEY is not needed with the enterprise setup. Setting RECAPTCHA_ENTERPRISE to 'true' switches the gem's verify call to the Enterprise Assessment API endpoint rather than the standard verify URL. Standard API keys and Enterprise API keys are issued from different Google interfaces and are not interchangeable.

## recaptcha_tags takes 11 options including a CSP nonce slot and 3 callback hooks

The recaptcha_tags helper accepts 11 options. Most have defaults that work without any configuration, but several address specific situations.

:theme sets the widget color scheme. Available choices are 'dark' and 'light'; the default is 'light'. :ajax switches to dynamic AJAX rendering and defaults to false. :noscript controls whether a noscript fallback tag is included; it defaults to true.

For Content Security Policy, :nonce sets a nonce attribute on the script tag. The README suggests SecureRandom.base64(32) to generate one, or content_security_policy_nonce if Rails config.content_security_policy_nonce_generator is already configured. Without a nonce, a strict CSP that blocks inline scripts will block the reCAPTCHA widget from loading.

3 JavaScript callback hooks are available as option names: :callback fires when the user completes a successful response, :expired_callback fires when the response expires before form submission and the user must re-verify, and :error_callback fires when reCAPTCHA encounters an error such as a network connectivity failure. Each option takes a JavaScript function name as a string rather than an inline function.

The :site_key option overrides the configured RECAPTCHA_SITE_KEY for a single form, which covers the case of multiple keys on one application. The :error option overrides the error code returned by the API.

## Rails 7 with Turbo and Stimulus has known compatibility issues

At the top of the Rails installation section the README flags a Rails 7 Turbo and Stimulus compatibility issue in bold and points to a repository wiki page for details. Standard reCAPTCHA integration assumptions break when Turbo drives page navigation, which is why the warning appears before the gem line rather than in a troubleshooting appendix.

The core problem is that reCAPTCHA v2's script registers once per full page load, but Turbo replaces page content without triggering a full reload. A widget inserted inside a Turbo frame or after a Turbo navigation may not be re-initialized by the previously loaded Google CAPTCHA script. Whether this affects a specific application depends on how forms are submitted and whether Turbo Stream or Turbo Drive is active for those routes, which is why the wiki carries the details.

For non-Turbo Rails apps the integration follows the standard view-and-controller pattern without any extra steps. Sinatra apps that use manual module inclusion bypass this problem entirely, since Sinatra has no equivalent navigation library.

## No GitHub releases, last push on 2026-06-19, MIT license dating to 2007

No GitHub releases exist in the repository, so there is no tagged version to reference in a bug report or to pin in a Gemfile with an exact version constraint. Gem version comes from RubyGems rather than repository tags, and the gem badge in the README links to rubygems.org/gems/recaptcha for what is current.

MIT license covers the code. Copyright in the README header runs from 2007 to 2013 under Jason L Perry. A CHANGELOG.md file at the repository root tracks version history. The last push was on 2026-06-19.

Because the gem wraps Google's reCAPTCHA API endpoints specifically, changes on Google's side such as a new verify URL, a new token format, or the deprecation of a reCAPTCHA version all require a corresponding update to the gem before apps remain functional. An app that depends on this gem inherits that dependency on Google keeping those endpoints available and compatible. Any migration away from Google reCAPTCHA means replacing this gem entirely, since no alternative CAPTCHA provider is covered.

## Conclusion

Use the recaptcha gem if you are adding Google reCAPTCHA to a Rails or Sinatra app and want a pre-built helper rather than writing the API calls yourself. Skip it if you need a vendor-neutral CAPTCHA solution, because the gem wraps only Google's reCAPTCHA service. Before you start, register a key in Google's admin console and note which type you chose, because v2 Checkbox, v2 Invisible, and v3 each use different methods and the keys are not interchangeable. On Rails 7 with Turbo and Stimulus, check the repository wiki before writing any view code, since the README flags known compatibility issues.

## FAQ

### How do I install the recaptcha gem in Rails?

Add gem 'recaptcha' to your Gemfile and run bundle install. Set RECAPTCHA_SITE_KEY and RECAPTCHA_SECRET_KEY as environment variables, then add recaptcha_tags to your view form and verify_recaptcha in the corresponding controller action.

### What is the difference between verify_recaptcha and verify_recaptcha! in the recaptcha gem?

verify_recaptcha returns true or false depending on whether the CAPTCHA check passes. verify_recaptcha! raises an exception on failure instead of returning false.

### Can I use v2 and v3 reCAPTCHA with the same API key?

No. Each reCAPTCHA type requires its own key registered in Google's admin console. Using a v2 method with a v3 key produces an 'Invalid key type' error from Google's API. Keys are type-specific and cannot be reused across types.

### Does the recaptcha gem support the reCAPTCHA Enterprise API?

Yes. Set RECAPTCHA_ENTERPRISE to 'true' and provide RECAPTCHA_ENTERPRISE_API_KEY and RECAPTCHA_ENTERPRISE_PROJECT_ID. The standard RECAPTCHA_SITE_KEY is still required for the enterprise key id; RECAPTCHA_SECRET_KEY is not used with enterprise.

### Does ambethia/recaptcha work with Sinatra?

Yes. Add gem 'recaptcha' to the Gemfile, set the environment variables, and manually include Recaptcha::Adapters::ViewMethods and Recaptcha::Adapters::ControllerMethods in the appropriate classes. A demo is provided in the demo/sinatra directory of the repository.

## Sources

- [ambethia/recaptcha on GitHub](https://github.com/ambethia/recaptcha)
- [Issues](https://github.com/ambethia/recaptcha/issues)
- [License: MIT](https://github.com/ambethia/recaptcha/blob/master/LICENSE)
- [Project website](http://github.com/ambethia/recaptcha)
- [README](https://github.com/ambethia/recaptcha/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ambethia-recaptcha
