← Blog

Validate an email address in Ruby

Ruby's standard library validates email syntax and checks MX records with zero gems — here's the code, plus where an API actually earns its place.

Contents

You want to validate an email in Ruby, and you are one search away from a 6,000-character regex someone pasted into a gist in 2011. Put it down. Ruby's standard library ships a maintained email regex and a DNS resolver, so you can check both the shape of an address and whether its domain can receive mail without adding a single gem. Here's the code, then the honest line about where it stops.

Syntax: the stdlib already has a regex

Ruby's uri library exposes URI::MailTo::EMAIL_REGEXP, an anchored pattern maintained by Ruby core alongside the rest of the URI parser. You do not write it, you do not paste it, and it gets fixed upstream when RFC edge cases surface.

require 'uri'

URI::MailTo::EMAIL_REGEXP.match?('you@example.com')  # => true
URI::MailTo::EMAIL_REGEXP.match?('you@@example.com')  # => false
URI::MailTo::EMAIL_REGEXP.match?('not-an-email')       # => false

match? returns a boolean and skips allocating a MatchData object, so it's the right call when all you want is a yes or no. The pattern is anchored (\A...\z), so it validates the whole string, not a substring — no need to bolt on your own anchors.

That's syntax done. For plenty of forms this is genuinely all you need: reject the typos, accept the rest, move on. But a syntactically perfect address can still point at a domain that has never received mail in its life. For that, you ask DNS.

Deliverability shape: MX lookup with Resolv

The resolv library — also stdlib — resolves DNS records without shelling out to dig or pulling a networking gem. An MX (mail exchange) record is how a domain advertises the servers that accept its email; if a domain has none, nothing is going to be delivered there. (If MX records are new to you, we wrote up what an MX record is.)

require 'resolv'

def domain_has_mx?(domain)
  Resolv::DNS.open do |dns|
    records = dns.getresources(domain, Resolv::DNS::Resource::IN::MX)
    records.any?
  end
end

domain_has_mx?('gmail.com')        # => true
domain_has_mx?('example.invalid')  # => false

getresources returns an array of MX records; any? is enough to answer "can this domain receive mail at all?". Wrap the two checks together and you have a stdlib-only validator:

require 'uri'
require 'resolv'

def deliverable_shape?(email)
  return false unless URI::MailTo::EMAIL_REGEXP.match?(email)

  domain = email.split('@', 2).last
  Resolv::DNS.open do |dns|
    dns.getresources(domain, Resolv::DNS::Resource::IN::MX).any?
  end
end

deliverable_shape?('you@gmail.com')        # => true
deliverable_shape?('you@example.invalid')  # => false

Zero gems. Syntax plus a live MX lookup covers the two failures that matter most: malformed input and dead domains.

When a gem earns its place: truemail

If you want those two checks bundled with configuration, retries, and a whitelist/blacklist, the truemail gem does it cleanly.

require 'truemail'

Truemail.configure do |config|
  config.verifier_email = 'verifier@yourdomain.com'
end

Truemail.validate('recipient@example.com', with: :mx).result.success  # => true

with: :regex runs syntax only; with: :mx adds the DNS check you just wrote by hand. truemail also offers with: :smtp, which opens a connection to the mail server to probe the mailbox — be honest with yourself about that one: major providers rate-limit or refuse it, results are inconsistent, and it can get your sending IP flagged. Syntax and MX are the reliable tier.

What syntax and MX still can't tell you

Here's the ceiling on everything above. A valid, MX-backed address can still be a burner from a disposable-email service, a role account like info@ or sales@ that no human reads, or a free-provider throwaway. Regex and DNS cannot see any of that — those judgments need lists that someone keeps current.

That's the layer Boundstone's API adds. The same syntax and MX you'd run yourself, plus disposable and role classification, returned over one HTTP contract you can call identically from a Rails controller, a Sidekiq job, or a serverless function.

require 'net/http'
require 'json'
require 'uri'

uri = URI('https://api.boundstone.io/v1/verify/email')
req = Net::HTTP::Post.new(uri)
req['Authorization'] = 'Bearer bs_live_YOUR_KEY'
req['Content-Type']  = 'application/json'
req.body = JSON.generate(email: 'you@example.com')

res  = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)

data['mx_found']                 # => true
data['disposable']               # => false
data['role_account']             # => false
data['checks']['performed']      # => ["syntax", "mx", "disposable_list", "role_list"]
data['checks']['not_performed']  # => ["smtp_mailbox", "catch_all"]

Every response carries checks.performed and checks.not_performed, and the second array is the point. Boundstone does not do smtp_mailbox (opening an SMTP connection to confirm the box exists) or catch_all detection. Plenty of services claim both; the results are unreliable and often blocked, so we list them as not performed rather than dress them up. A pass from us means exactly the four checks in performed ran — no more. The response also flags free_provider, though that flag isn't counted as one of the checks.

Want to eyeball an address before writing any Ruby? Paste one into the keyless email validator.

The short version

  • Syntax: URI::MailTo::EMAIL_REGEXP.match?(email) from stdlib. No gem.
  • Domain can receive mail: a Resolv::DNS MX lookup. Also stdlib.
  • Those two cover most real validation. Ship them first.
  • Want it bundled: truemail with :regex/:mx. Skip :smtp.
  • Want disposable/role plus one contract across your services: the API — and read checks.not_performed so you know exactly what a "valid" did and did not verify.

Working in more than one language? The same approach in Python is here.

Frequently asked questions

How do I validate an email address in Ruby?

In Ruby you can check basic shape with a regex or the built-in URI::MailTo::EMAIL_REGEXP, but that only confirms the string looks like an address. To learn whether the domain can actually receive mail, call a validation API from Ruby using Net::HTTP or Faraday and read the JSON response. Boundstone's email check returns valid_syntax, mx_found, disposable, role_account, and free_provider, and its free tier gives 250 checks a month with no card, so you can wire it into a Ruby script or Rails model without signing a contract.

Does a Ruby regex like URI::MailTo::EMAIL_REGEXP prove an email is real?

No. A regex only validates syntax, so it cannot tell you whether the domain exists or whether the address is disposable, a role account, or from a free provider. Boundstone layers an MX lookup over DNS plus disposable-domain and role-account checks on top of syntax, so a valid result means the address is well-formed and its domain is configured to receive mail. It deliberately does not perform SMTP mailbox probing or catch-all detection, and every response lists exactly which checks ran and which did not.

How can I check whether an email address actually exists in Ruby?

No service can guarantee that a specific mailbox exists without probing the mail server, and Boundstone deliberately does not do SMTP mailbox verification or catch-all detection, reporting both as not_performed in every response. What you can confirm from Ruby is that the address is syntactically valid, its domain publishes MX records, and whether it is disposable, a role account, or a free provider. That honesty contract means a valid result tells you the address can receive mail in principle, not that a real person is behind it, so you know precisely what you are and are not relying on.

Thomas Tsui

Founder of Boundstone — building phone, email and IP validation you can actually verify.

One honest API for email, phone and IP — every response lists what it checked and what it didn't claim to. Free tier: 250 credits/month, no card, credits never expire.

More from Boundstone — API documentation · Benchmark methodology · The benchmark series · Buyer's checklist