How to validate an email address in Ruby

Last Updated Nov 16, 2021
Emma Jagger

Engineer, maker, Google alumna, CMU grad

Table of Contents:

If you use email marketing for your business or send emails to mailing lists, you are probably familiar with the challenge of having bad or fake email addresses in your database.

Every time you send a bad email, you receive a notification that your message has been rejected, which is more than just an annoyance and represents a significant risk.

A developer should never trust users' data and must test every form field before saving them in the database. Fortunately, Ruby on Rails provides several mechanisms that allow you to perform these validations efficiently.

Don't reinvent the wheel.
Abstract's APIs are production-ready now.

Abstract's suite of API's are built to save you time. You don't need to be an expert in email validation, IP geolocation, etc. Just focus on writing code that's actually valuable for your app or business, and we'll handle the rest.

Get started for free

The biggest risk: having too many fake emails in your mailing list

Service providers check your bounce rate. If they find that you are sending too many emails to the wrong addresses, they may blacklist you. This can have even greater implications than spam filtering, as many of your recipients' mail servers could reject every single mail you send. Once your servers are blacklisted, it can be complicated to remove the blacklisting.

You can verify your server's blacklisting status on a service like Debouncer.

How to validate fields in Ruby on Rails

In most cases, the fields of a form are injected into an ActiveRecord model, which performs the validation. The ActiveRecord::Validations API allows to easily define rules to validate various aspects of these fields: format, inclusion, length, uniqueness, and many others.

Let's take as an example a contact form allowing your visitors to send a message to your staff. Such a form would contain Last and First Name, Email Address, and Message fields. Let's write a model and its validation mechanism to check the presence and length of each field and the email address format. Here is the code:


class ContactData < ApplicationRecord
  validates_presence_of :first_name, :last_name, :email, :message
  validates_length_of :first_name, :last_name, maximum: 150
  validates_format_of :email, with: A_MAGIC_REGEXP
end

ActiveRecord calls all validation methods defined in the model during the saving process. If they are successful, then the model is saved in the database. But if there is at least one error, the model won't be saved in the database. You can then check the content of its errors field to obtain details about the validation errors.

Here are the details of the methods used in the code above:

  • validates_presence_of: check that the fields contain something. Note that if the value for this field is a simple space, the validation will succeed.
  • validates_length_of: checks the length of the value. The acceptable length of a field depends on the application's needs and the database's field type.
  • validates_format_of: checks that the format of the field value matches the regular expression provided in the parameter

While the first two validation methods are easy to understand, the third requires a little more attention.

How to use the validates_format_of method

The first step of email validation consists of checking the format of the email address. To do this, it is sufficient to use the validates_format_of method mentioned earlier in this document along with an efficient regular expression.

The standard Ruby library provides the URI::MailTo class, which declares the regular expression EMAIL_REGEXP. This is suitable in most cases.


URI::MailTo::EMAIL_REGEXP
=> /\A[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\z/

Here is how to use it in our model:


class ContactData < ApplicationRecord
  validates_presence_of :first_name, :last_name, :email, :message
  validates_length_of :first_name, :last_name, maximum: 150
  validates_format_of :email, with: URI::MailTo::EMAIL_REGEXP
end

How to validate an email address in Ruby

As a developer, you are looking to implement the best way to validate an email address format. To do so, you first need to understand precisely what format is allowed by Internet Standards. Section 3,2,4 of RFC 2822 specifies that the local string (the part of the email address that comes before the @) can contain the following characters: ! $ & * - = ` ^ | ~ # % ' + / ? _ { } You can also use something like a regular expression (regex) to verify the email structure is correct.

According to this, validation of an email address format requires quite a complex mechanism. Luckily the URI::MailTo class, which is part of the Ruby Standard Library, contains the URI::MailTo::EMAIL_REGEXP constant, which can be used for email format validation, so you don't have to create your own.

CODE TO BE DISPLAYED ON PAGE GOES HERE

"mymail@myserver.com" =~ URI::MailTo::EMAIL_REGEXP
=> 0 # Successful validation

"wrong" =~ URI::MailTo::EMAIL_REGEXP
=> nil # Not a valid email address

However, according to the next section of RFC 2822, an email address can contain any characters as long as quotes escape them. As an example, this is a correct email address:


"Some spaces! And @ sign too!" @some.server.com

And the URI::MailTo::EMAIL_REGEXP constant would reject it.

At this point, you could conclude that URI::MailTo::EMAIL_REGEXP is able to validate 99% of all emails submitted to your forms, which may be good enough.

But an address like sefgqpouinqlzoiyhqjhzgf@gmail.com has a correct syntax and would be validated by any regular expression you may implement. Chances are, though, that the mailbox does not exist, and every email sent to this address would bounce back.

Check that an email address exists in Ruby on Rails.

The method for checking the existence of an email address is to extract the server FQDN (the part after the @ sign); to query the corresponding DNS servers to confirm that the server exists; to query the MX records from the DNS entries. A well-made verification method will also match the server name against a list of known disposable email services to determine if the visitor is using a disposable email address.

Setting up such a complex verification method is beyond this document's scope. But fortunately, there is a solution, easy to implement, which we will discuss in the next chapter.

APIs: The real solution to validating email addresses

An efficient email checker would not only verify the address format but also verify if the email is hosted by a free or paid service, if the domain correctly contains an MX record and if it points to a valid SMTP server, if the email domain is linked to a disposable email service, and more.

Abstract is a fast and reliable API provider that offers multiple services, including email validation. Once subscribed, you get free access to the API along with your private API key.

Abstract Email Validation API is accessible through a GET request and takes 2 arguments. Here is its format:


uri = "https://emailvalidation.abstractapi.com/v1/?api_key=#{api_key}&email=#{amail_eddress}"

Using Net::HTTP, here is how to make the request and retrieve the response:


require 'net/http'
require 'net/https'

api_key = "YOUR_API_KEY"

def make_abstract_request(email_address)
  uri = URI("https://emailvalidation.abstractapi.com/v1/?api_key=#{api_key}&email=#{email_address}")

  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER

  request =  Net::HTTP::Get.new(uri)

  response = http.request(request)

  return response.body
rescue StandardError => error
  puts "Error (#{ error.message })"
end

make_abstract_request("email@to.validate")

The response is in JSON format and contains multiple parameters:

  • auto_correct: contains a suggestion of the correct email if a typo error has been detected
  • is_valid_format: check the email format and indicates if it is correct
  • is_free_email: indicates if the email is hosted by a free service (Gmail, Outlook, ...)
  • is_disposable_email: indicates if the email address is provided by a disposable email provider, which is a service allowing people to receive email without any authentication nor identification
  • is_role_email: indicates if the email recipient seems to be a team or a department instead of a person
  • is_catchall_email: indicates if the email lands in the catchall inbox of the SMTP server
  • is_mx_found (only available on paid plans): indicates if the domain provides one or more MX records and if they point to a server
  • is_smtp_valid (only available on paid plans): check the SMTP domain and indicate if the verification was successful
  • quality_score: a number scaling from 0.01 to 0.99, indicating the level of trust of the email address submitted

Here is an example of a response:


{
  "email": "johnsmith@gmail.com",
  "autocorrect": "",
  "is_valid_format": {
        "value": true,
        "text": "TRUE"
        },
  "is_free_email": {
        "value": true,
        "text": "TRUE"
        },
  "is_disposable_email": {
        "value": false,
        "text": "FALSE"
        },
  "is_role_email": {
        "value": false,
        "text": "FALSE"
        },
  "is_catchall_email": {
        "value": false,
        "text": "FALSE"
        },
  "is_mx_found": {
        "value": true,
        "text": "TRUE"
        },
  "is_smtp_valid": {
        "value": true,
        "text": "TRUE"
        },
  "quality_score": 0.90,
}

Abstract's Email Validation API comes with Ruby libraries, code snippets, guides, and more.

Get started for free
Abstract's Email Validation API comes with Ruby libraries, code snippets, guides, and more.
Get started