Bot Challenge Page
This page documents the implementation of bot_challenge_page in GeoDiscovery, including rationale, configuration, and usage details.
Overview
bot_challenge_page provides Turnstile-based bot protection with session awareness. It was chosen to replace Rack::Attack due to its greater resilience against evasive bot tactics (e.g., IP rotation).
Our deployment uses Cloudflare Turnstile to issue JavaScript-based challenges and track exemption status via session cookies.
Why We Removed Rack::Attack
The previous setup used Rack::Attack to rate-limit suspicious requests. However, this approach proved ineffective against rotating IPs and occasionally blocked legitimate traffic.
Rack::Attack remains in the Gemfile but is not initialized.
Dependencies
# Gemfile
gem "bot_challenge_page", "~> 1.1.0"
gem "rack-attack", "~> 6.7" # present but unused
Controller Integration
As of GeoDiscovery v4.5.6, the app follows the gem’s 1.x integration style:
ApplicationControllerincludes the shared bot-challenge behaviorCatalogControllerapplies protection to search and index traffic- Challenge exemptions are expressed through
skip_whenrules instead of the older inlinebefore_actionexample
Configuration
All challenge behavior is configured in:
config/initializers/bot_challenge_page.rb
Key settings include:
config.enabled— UsesSettings.turnstile.enabled(disabled in test env)config.cf_turnstile_sitekeyand..._secret_key— Stored inSettings.turnstileconfig.redirect_for_challenge = false— Render challenge inlineconfig.skip_when— Defines requests that should bypass the challenge flow- IP safelists parsed from
TURNSTILE_IP_SAFELISTas CIDR ranges
Session Exemptions
We track exemptions using session state. If a user passes a Turnstile challenge, they are considered exempt for the rest of the session.
Additional exemption logic is now centered on skip_when conditions in the initializer. In practice this ensures:
- Search interface components (like facets) are not interrupted
- Known IP ranges (set via
TURNSTILE_IP_SAFELIST) are exempt
Testing and Local Development
See Cloudflare’s testing guide for keys that always pass, challenge, or fail.
In development and test, the feature is typically disabled:
config.enabled = !Rails.env.test? && Settings.turnstile.enabled
When documenting or debugging the feature, treat the initializer as the source of truth for:
skip_whenexemptions- Turnstile site and secret keys
- CIDR safelist parsing
- Inline challenge rendering behavior
Related Links
- Production Turnstile Widget Dashboard
- Uptime Robot status page
- bot_challenge_page GitHub
- Cloudflare Turnstile Docs
- GeoDiscovery GitHub