It's been a bit over 3 years since my last article on reCAPTCHA and a lot of things have changed. So I think it's time to revisit the subject. Note that the old reCAPTCHA article will soon redirect here. Also expect a sample project to be posted to GitHub very soon.

Introduction To reCAPTCHA

Spam is a HUGE issue for websites. Scripted robots created by spammers crawl around the web constantly, trying to spam different form fields in hopes to get their spam messages out. Luckily there is an easy way to combat these spammers using a service called reCAPTCHA. reCAPTCHA is a free service provided by Google that attempts to detect legitimate visitors using a number of ever changing techniques. If it is unable to determine whether a visitor is legitimate or not, it asks the user to solve a simple puzzle to prove that they are human.

I am not a robot
The image users get when they are known to be legitimate.

reCAPTCHA puzzle
If reCAPTCHA has doubts the user is legitimate, it displays a puzzle like the one above after they check the I'm not a robot checkbox.

reCAPTCHA Site Setup

Before we can utilize reCAPTCHA in our application, we must first set up a new site. Sign into your Google account and visit the reCAPTCHA admin page.

reCAPTCHA Admin Page

You should see a page similar to the one listed above. Under the section labeled Register a new site fill in the label with a descriptive value. For the domains, list all applicable domains you will be using reCAPTCHA on. For example, if your website is example.com, make sure to list example.com. Also include localhost as well as any development, staging, or QA servers that you will use. For internal servers, simply use the internal IP for the domain. Once finished, click the Register button.

reCAPTCHA Add Site

After clicking register, you will be taken to a page listing the site key and secret key. Make sure to take note of these values, you will need them later on.

reCAPTCHA site key

Now we can work on our Rails application!

Rails Application Setup

This example will be a simple new user signup form that allows the user to provide a name, email address, and password to create a new account. This signup form is not unlike signup forms you've run into before on various websites. Our signup form will have the reCAPTCHA checkbox. To make integration easier, we will use the recaptcha gem. The recaptcha gem provides a few helpers that makes integration much easier.

First, we need to add the recaptcha gem to our Gemfile. We also need to add in the bcrypt gem for the purposes of this example. If you are integrating reCAPTCHA into your own application, you don't need to add bcrypt. Open up your Gemfile and add in the lines listed below.

Gemfile:
  
    gem 'recaptcha', require: 'recaptcha/rails'
    gem 'bcrypt'
  

Now run a bundle install to install the gem.

Terminal Commands:
  
    bundle install
  

Now we need to create an initializer to configure the recaptcha gem. Create a new file in config/initializers called recaptcha.rb and modify it so that it looks like the code listed below.

config/initializers/recaptcha.rb
  
    Recaptcha.configure do |config|
      config.site_key  = Rails.application.secrets.recaptcha_site_key
      config.secret_key = Rails.application.secrets.recaptcha_secret_key
    end
  

In the above initializer, we set the site key and secret key to the values contained in the Rails config/secrets.yml file.

For your application do NOT store these values directly in secrets.yml unless you add your secrets.yml file to your .gitignore file. Instead, use environment variables.

Now for the config/secrets.yml file. In this example application, I am pulling the secret key and site key from environment variables. Open up your secrets.yml file and modify it to include your site key and secret.

config/secrets.yml
  
# Be sure to restart your server when you modify this file.

# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!

# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
# You can use `rails secret` to generate a secure secret key.

# Make sure the secrets in this file are kept private
# if you're sharing your code publicly.

development:
  secret_key_base: 2f94601049a14193ca603923d770a0189c82d3746fbdb3aa472780791dc1e0c4048f473a1004a829a10d0aaa0abfc0f061282a51eba05af926849d772bac022a
  recaptcha_site_key: <%= ENV["RECAPTCHA_SITE_KEY"] %>
  recaptcha_secret_key: <%= ENV["RECAPTCHA_SECRET_KEY"] %>

test:
  secret_key_base: 58150241e15c621ffba28e32cd67928e54a78dfe722371c3e52b740eb7ebb5c05a02cd10071b3661af6771d1aca8f41453f6315e807c61d3f8c5e8e1b8c7adb7

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  recaptcha_site_key: <%= ENV["RECAPTCHA_SITE_KEY"] %>
  recaptcha_secret_key: <%= ENV["RECAPTCHA_SECRET_KEY"] %>
  </code>

Great, now let's build out our example. This example app will contain a single model called User. The user model will contain three fields. The first, name, is just a name for the user. The second, email, stores the email address of the user. The third, password_digest, is used by bcrypt to store a salted and hashed password. If those terms are unclear to you, don't worry, it isn't necessary to understand them for this article. We will cover bcrypt and the terms mentioned in a future revised article about building authentication from scratch. For now, run the commands listed below to create your user model and migrate the database.

Terminal Commands
  
    rails g model user name email password_digest
    rake db:migrate
  

Now we need to add some code to our user model. Once again, don't worry about the details. The important thing we are doing here is adding validations to demonstrate how reCAPTCHA works when the page is re-rendered. The has_secure_password is a magic function that bcrypt provides to inject it's code into our model.

app/models/user.rb:
  
    class User < ApplicationRecord
      has_secure_password
      validates_presence_of :password, on: :create
      validates :email, uniqueness: true, presence: true
    end
  

Now let's create a controller and our views. We will create a controller called Users with two actions, new and create. The new action will render the signup form, and the create action will process it. Run the commands listed below to create the users controller now.

Terminal Commands:
  
    rails g controller users new create
  

Excellent, now let's change our routes file. Open up the config/routes.rb file and modify it so that it looks like the code listed below.

config/routes.rb
  
    Rails.application.routes.draw do
      resources :users, only: [:new, :create]
      root to: 'users#new'
    end
  

First, we specify that users a resource based controller. This just sets up some default routes for the new and create actions. Second, we set the site root to automatically go to /users/new. This is just for convenience.

Before we add code to our users controller, let's create our users/new view first. Open up the app/views/users/new.html.erb file and modify it so that it looks like the code listed below.

app/views/users/new.html.erb
  
    <h3>New User Sign Up</h3>
    <% if [email protected]? %>
      <ul>
        <% @user.errors.full_messages.each do |message| %>
          <li><%= message %></li>
        <% end %>
      </ul>
    <% end %>
    <%= form_for User.new do |f| %>
    <div>
      <%= f.label :name %>
      <%= f.text_field :name %>
    </div>
    <div>
      <%= f.label :email %>
      <%= f.text_field :email %>
    </div>
    <div>
      <%= f.label :password %>
      <%= f.password_field :password %>
    </div>
    <div>
      <%= f.label :password_confirmation %>
      <%= f.password_field :password_confirmation %>
    </div>
    <div>
      <br />
      <%= recaptcha_tags %>
      <br />
    </div>
    <div>
      <%= f.submit "Sign Up" %>
    </div>
    <% end %>
  

The form is a pretty standard form with the exception of line 28. line 28 calls a special helper provided by the recaptcha gem. This helper sets everything up for using reCAPTCHA. Since we are editing views, let's create our create view next. This view is only shown to the user if they have successfully submitted the form. Open up the app/views/users/create.html.erb file and modify it so that it looks like the code listed below.

app/views/create.html.erb
  
<h3>Thank You!</h3>
<p>
Thanks for signing up!
</p>
  

Now for the final piece of the puzzle. Open up the users controller at app/controllers/users_controller.rb and modify it so that it looks like the code listed below.

app/controllers/users_controller.rb
  
    class UsersController < ApplicationController
      def new
        @user = User.new
      end
    
      def create
        @user = User.new(user_params)
        if !verify_recaptcha(model: @user) || [email protected]
          render "new"
        end
      end
    
    private
      def user_params
        params.require(:user).permit(:name, :email, :password, :password_confirmation)
      end
    end
  

Line 8 calls another helper method provided by the recaptcha gem called verify_recaptcha. The verify_recaptcha method checks whether the reCAPTCHA information the user provided was valid. If it was not, the new is rendered again with an error message that the recaptcha gem provides. If reCAPTCHA returns success and the form is valid, the create view gets rendered, which indicates success.

Thanks for reading!