Ruby Gem
The eu_captcha gem is the official Ruby client for server-side token verification. It has no runtime dependencies (uses Ruby's built-in net/http), wraps the verification API with configurable fault-tolerance, and supports automatic IP and User-Agent resolution from Rack environments.
- RubyGems:
eu_captcha - License: BSD-2-Clause
- Requires: Ruby ≥ 2.7
Installation
Add to your Gemfile:
gem 'eu_captcha'
Then run:
bundle install
Or install directly:
gem install eu_captcha
Basic usage
require 'eu_captcha'
captcha = EuCaptcha::Client.new(
sitekey: ENV['EUCAPTCHA_SITE_KEY'],
secret: ENV['EUCAPTCHA_SECRET_KEY']
)
result = captcha.validate(
token: params['eu-captcha-response'],
remote_addr: request.ip
)
render_error unless result.success?
Replace EUCAPTCHA_SITE_KEY and EUCAPTCHA_SECRET_KEY with the values from your sitekey configuration. Store them in environment variables — do not hardcode them.
validate method
result = captcha.validate(
token: nil,
remote_addr: '',
user_agent: '',
rack_env: nil
)
All parameters are optional keyword arguments:
| Parameter | Type | Description |
|---|---|---|
token |
String, nil |
The value of the eu-captcha-response POST field. Pass nil or omit when using rack_env — an empty string is sent to the API in either case. Always submit it regardless. |
remote_addr |
String |
The visitor's IP address. When empty, resolved from rack_env (respecting check_cdn_headers). |
user_agent |
String |
The visitor's User-Agent header. When empty, read from rack_env['HTTP_USER_AGENT']. |
rack_env |
Hash, nil |
A Rack environment hash. When provided, remote_addr and user_agent are resolved automatically if not supplied explicitly. |
Configuration reference
All options are keyword arguments to EuCaptcha::Client.new.
| Option | Type | Default | Description |
|---|---|---|---|
sitekey |
String |
— | Required. Your public sitekey. |
secret |
String |
— | Required. Your secret key. Never expose this client-side. |
fail_default |
Boolean |
true |
Return value used when the API cannot be reached. true = fail open (allow on error); false = fail closed (deny on error). |
check_cdn_headers |
Boolean |
true |
When true, the client IP is resolved from HTTP_CLIENT_IP, HTTP_X_FORWARDED_FOR (first entry), and HTTP_X_REAL_IP in the Rack environment before falling back to REMOTE_ADDR. Set to false when not behind a proxy, or when you always pass remote_addr explicitly. |
verify_url |
String |
(production) | Override the verification endpoint. Useful for testing. |
credentials_url |
String |
(production) | Override the verify-credentials endpoint. |
Raises EuCaptcha::Error if sitekey or secret are empty.
Result object
validate returns an EuCaptcha::Result with these methods:
| Method | Returns | Description |
|---|---|---|
success? |
Boolean |
true if the API was reachable, the token was valid, and train is not set |
success_network? |
Boolean |
true if the API request completed without a network or HTTP error |
success_token? |
Boolean |
true if the API confirmed the token as valid (raw success field from the response) |
train |
Boolean, nil |
true if validation was bypassed, false in normal operation, nil on network failure |
Use success_network? and success_token? individually when you need to distinguish between a network failure and a genuinely invalid token:
result = captcha.validate(token: params['eu-captcha-response'], remote_addr: request.ip)
unless result.success_network?
# Could not reach the API — consider logging or alerting
end
unless result.success_token?
# Token was rejected — the submission is likely automated
end
Fault tolerance
By default (fail_default: true) the client fails open: if the verification API is unreachable, success? returns true and the submission is accepted. This avoids locking out legitimate users during transient network issues.
Set fail_default: false for endpoints where you prefer to reject submissions when the API cannot be reached:
captcha = EuCaptcha::Client.new(
sitekey: ENV['EUCAPTCHA_SITE_KEY'],
secret: ENV['EUCAPTCHA_SECRET_KEY'],
fail_default: false # reject on network failure
)
Choose the right trade-off for your use case:
| Scenario | Recommended setting |
|---|---|
| Contact / signup forms — prefer availability | fail_default: true (default) |
| High-security endpoints — prefer strictness | fail_default: false |
Verifying credentials
Use verify_credentials to confirm your sitekey and secret are valid without submitting a client token. Useful for startup or configuration checks:
captcha = EuCaptcha::Client.new(
sitekey: ENV['EUCAPTCHA_SITE_KEY'],
secret: ENV['EUCAPTCHA_SECRET_KEY']
)
unless captcha.verify_credentials
# Credentials are invalid or the API is unreachable — log and alert
end
verify_credentials returns false on any network or API error rather than raising, so it is safe to call during application initialisation.
Rails
Store credentials in config/credentials.yml.enc (or .env with the dotenv-rails gem) and create a client in an initializer:
config/initializers/eu_captcha.rb
EU_CAPTCHA = EuCaptcha::Client.new(
sitekey: Rails.application.credentials.dig(:eucaptcha, :sitekey),
secret: Rails.application.credentials.dig(:eucaptcha, :secret)
)
Controller
class ContactController < ApplicationController
def create
result = EU_CAPTCHA.validate(
token: params['eu-captcha-response'],
remote_addr: request.ip,
user_agent: request.user_agent
)
unless result.success?
render json: { error: 'CAPTCHA verification failed' }, status: :unprocessable_entity
return
end
# process the form...
end
end
request.ip respects Rails' trusted-proxy configuration, so the real visitor IP is forwarded correctly when running behind a CDN or load balancer.
Sinatra / Rack
Pass the Rack environment directly and let the gem resolve the client IP and User-Agent automatically:
require 'sinatra'
require 'eu_captcha'
CAPTCHA = EuCaptcha::Client.new(
sitekey: ENV['EUCAPTCHA_SITE_KEY'],
secret: ENV['EUCAPTCHA_SECRET_KEY']
)
post '/contact' do
result = CAPTCHA.validate(
token: params['eu-captcha-response'],
rack_env: env
)
halt 422, 'CAPTCHA verification failed' unless result.success?
# process the form...
end
When rack_env is provided, validate resolves the client IP and User-Agent automatically, respecting the check_cdn_headers setting.