Skip to content

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.