Validating Emails in Rails (and other web frameworks)

Validating emails is an apparently easy task that turns out to actually be quite difficult.

Early in your carreer as a rails developer you'll find yourself needing to validate email addresses on one of your models. Which is simple, right? It only takes one line.

validates :email, :format => /EMAIL_REGEX_GOES_HERE/

Now all you need to do is come up with a regex that allows all valid emails and rejects all invalid emails. Should me simple, right?

The first time I needed to validate an email address, I spent way too much time working on a regular expression. Eventually I came up with one that covered all the cases I though were valid and rejected the invalid ones I tested with. Feeling happy with my sexy regex, I deployed it, moved onto something else and never gave it another thought.

Then a few weeks later, I got an email from the client saying that one of their users couldn't create an account on the system. Turns out that emails can have all sorts of funky variations, some of which are part of the defined standard, some of which are not.

And so I learned something - there's something in the developer mindset that is bothered by the idea of people submitting invalid email addresses. The idea that  my regex could allow some invalid cases through bothered me, and I enjoyed the puzzle of coming up with a regular expression that would validate my example email addresses.

But lets take a step back and think about the purpose of validating an email. The main thing you want to catch is the case when a user accidentally mistypes their email or doesn't realize that they need to provide an email (for example, they enter a name or username instead).

Your app is not the email police, sent out into the world to punish users who have invalid or unusual characters in their email address. You just want to gently nudge the user if they enter something that is obviously not an email address, and otherwise let them get on with it.

Rejecting a single users real email address is many times worse than allowing multiple invalid emails into your database. You want to minimize false positives and who cares about false negatives.

And to that end, you don't really need a complicated regular expression.  You just need one that recognizes when something vaguely along the lines of something@somewhere.someplace. And that's enough. If ensuring that your user enters a valid email is valid is important to your app, then send them a confirmation email. That's the only way to ensure they've entered their valid email address.

So, getting back to the technical details. What regex should we use to validate our emails? Lets look at a few common examples that I collected(sources are in the table below). We'll evaluate these regexes against this list of valid and invalid emails.

Here's a quick 'n' dirty script to check a few email validation regular expressions.

And here's the results:

Regex Valid emails rejected (17) Invalid emails accepted (20) Source
/@/ 0 18
/.+@.+\..+/i 0 16
/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i 5 9
/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i 5 0
/^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i 5 7
/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 6 7
/\A[^@\s]+@([^@.\s]+\.)+[^@.\s]+\z/i 3 10
/\A[^@\s]+@([^@.\s]+\.)*[^@.\s]+\z/i 3 11
/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i 7 7
/\A(\S+)@(.+)\.(\S+)\z/i 1 13


The important thing to take away from these results is that none of these regular expressions are very good at both accepting valid emails and rejecting invalid ones. And since I think it's much worse to reject a valid emails than to accept an invalid one, that just leaves us with one of the first two. i.e. the simplest ones on the list.

In fact, it's probably better to validate email on the client side, but still allow the user to submit the email even if your validation thinks it's invalid (maybe with minimal server-side validation such as /@/). That way you can give them a helpful hint if it looks like they've entered an invalid email, but still allow the people with unusual emails to submit them.

Here are some related links: