RFC-compliant email address validator

Quick links: Source code | Email address validation service

There’s a gazillion regular expressions out there that claim to validate an email address. They don’t. Doug Lovell explains why here. The function that Doug made for his article is good, but it delegates the validation of the domain part of the address to the DNS servers of the world. This is a good approach but there are three issues with it:

  1. The RFCs reluctantly allow you to use an IP address rather than a domain name, so you need to check for that.
  2. The DNS may not be available to your function at the time it needs to check the address (maybe it’s an intranet application)
  3. There’s no need to add extra workload to the DNS servers of the world if the address is wrongly formatted in the first place.

So I’ve come up with a PHP function that validates all parts of a given email address, according to RFCs 112323963696429143435321 5322. I’ve released it under a license that allows you to use it royalty-free in commercial or non-commercial work, subject to a few conditions (Doug Lovell’s function is All Rights Reserved by Linux Journal so you can’t use it for anything).

It’s almost certainly the first email address validator that correctly lets you put an IPv6 address in for the domain part…

I’ve also included a lot of unit tests, including all Doug Lovell’s examples and all the examples of valid addresses in RFC 3696. You might try running these test past your current email address validator. Prepare for the same nasty surprise I got :-)

The source code is available here.

Quick links: Source codeEmail address validation service

34 thoughts on “RFC-compliant email address validator

  1. Hi Dominic,

    This is exactly what I’m after :-)

    This is my code so far, how do I implement your code into it?

    I’m a PHP novice, so just getting to grips with things at the moment.

    And this is at the end of my HTML:

    Please correct the following error:

    Many Thanks


  2. Hi Scott,

    Looks like some of your comment got mangled. All you need to do to implement this is copy-and-paste the is_email function into your code (somewhere near the top) and then you can validate an email address like this:

    $valid = is_email($email);

    $valid will be either true or false depending on whether $email is a valid email address.

  3. Hi Dominic,

    Thanks for getting back to me so quickly.

    Where exactly should I place ‘$valid = is_email ($email);’?

    Here is a link to my source code:


    As you can see I’ve got basic email validation:

    /* If e-mail is not valid show error message */
    if (!preg_match(“/([w-]+@[w-]+.[w-]+)/”, $email))

    show_error(“E-mail address not valid”);

    which I would like to replace with your code.

  4. Dear Dominic

    thanks for the nice code.

    I am actually on your website from http://www.linuxjournal.com/article/9585 after seeing your link in the comment.

    I am surprised to see and know that many different symbols can be alowed in the email address. That code didnot work for me.

    So I tried your code, it showed 1, and when I checked with an echo statement, it ECHOED outcome in TEXT.

    I am happy to see email validation is successful for 4 lettered tld like .info, .name atlast.

    Upon this success, I had enabled $checkDNS = true and tried an unexisting email address based on unexisting domain name, it still shows VALID


    getting to your notice, so, as this is fixed, with your permission, I wish to use this in our website, thank you

    With Best Regards
    Raghu Veer

  5. Very nice validator, thanks for going to the trouble of writing it. After seeing Doug Lovell’s article, I tested our existing email validators and found them to be deficient, so I was very happy to find your post here. It will save me a bit of time rewriting the validator from scratch :)

  6. Hello Dominic,

    I tested your code against the list of email addresses provided in Doug Lovell’s article. Your code does not accept all the addresses that Doug says are valid.
    Those that are rejected by your code are:

    abc@def@example.com is not valid.
    abc@example.com is not valid.
    Fred Bloggs@example.com is not valid.
    Joe.Blow@example.com is not valid.
    Doug “Ace” Lovell@example.com is not valid.

    Why do you think you code is refusing them? Are they valid as Doug claims?

    Also, your code generated a number of PHP notices when I ran them using PHP 5.2.9

    PHP Notice: Uninitialized string offset: 0 in /tmp/validEmail.php on line 131

    PHP Notice: Uninitialized string offset: -1 in /tmp/validEmail.php on line 142

    PHP Notice: Uninitialized string offset: 0 in /tmp/validEmail.php on line 285

    PHP Notice: Uninitialized string offset: -1 in /tmp/validEmail.php on line 296

    Those lines are:
    131: if ($element[0] === ‘(‘) {
    142: if ($element[$elementLength - 1] === ‘)’) {
    285: if ($element[0] === ‘(‘) {
    296: if ($element[$elementLength - 1] === ‘)’) {

    When $elementLength has a value of zero (0), the lines 142 and 296 are trying to get the character at position -1. That position cannot exist in a string.

    Changing the lines like this would be better I think:

    131: if (isset($element[0]) && $element[0] === ‘(‘) {
    142: if ($elementLength > 0 && isset($element[$elementLength - 1]) && $element[$elementLength - 1] === ‘)’) {
    285: if (isset($element[0]) && $element[0] === ‘(‘) {
    296: if ($elementLength > 0 && isset($element[$elementLength - 1]) && $element[$elementLength - 1] === ‘)’) {

    What do you think?

    • Hi Christian and thanks for the feedback. I’ll work on getting rid of those PHP notices for the next version.

      The addresses that Doug says are valid are not valid! He was citing addresses from the original version of RFC 3696. This RFC was later corrected by its author (see the errata here: http://www.rfc-editor.org/errata_search.php?rfc=3696&eid=246)

      Unfortunately the RFC online system only shows you the original version not the corrected one. I’m sure this led Doug Lovell to make the mistakes he made in his article.

  7. Pingback:   New and noteworthy open source email implementations | On Message with Ben Gross

  8. This is really sweet Dominic; Thank You!

    I am having a problem understanding why .comt and other inappropriate tld’s are considered valid. syntactically, they meet the criteria, but that’s not good enough. Is is best to simply use an array with all existing values. Not very maintainable. What are other thoughts about this?

    • Hi Scott,

      There’s no authoritative source for a list of TLDs. The list doesn’t change much but I wonder how many people who implemented a validator 5 years ago now allow .museum?

  9. Hi Dominic,

    I like your photo, just a man looking happy at the world and donating code to it :-)

    I’ve wrapped your code in a form email validation helper for the great & open source Concrete 5 CMS. While testing it I noticed the following bug (?) running the code on my server (PHP 5.2.6-1+lenny3 with Suhosin-Patch (cli) on Debian Lenny / Apache 2.2):

    The DNS check would ALWAYS evaluate to true. (A problem which Raghu Veer noticed on his setup, too).

    Investigating further into the problem, I found out that actually

    checkdnsrr(‘nawr2312312321312ds.de.’, ‘A’);

    would also return true – it’s not your code, it’s the implementation of this function in our versions probably.

    Luckily, someone in the PHP website pointed out a solution to this:
    You have to add a point to the domain name, thus modifying your code like so:

    // Check DNS?
    if ($checkDNS && function_exists(‘checkdnsrr’)) {
    $domain = $domain . ‘.’;
    if (!(checkdnsrr($domain, ‘A’) || checkdnsrr($domain, ‘MX’))) {
    return $diagnose ? ISEMAIL_DOMAINNOTFOUND : false; // Domain doesn’t actually exist

    He claims adding the dot enforces the root – it’s working now anyways :-)

    Kudos to you for writing this great email validation tool, complete with DNS checking and error messages.
    If you’re interested in the Validation Helper, I could send you a link to it.


  10. Hi, Dominic,

    I’ve been using version 15 to validate my quote form (http://messengerwebdesign.com/?page=request-quote) for some time now. Last week I tried replacing it with v17 and it stopped my form processing in its tracks. Any suggestions, or instructions for implementing? Could it have anything to do with the ‘diagnose’ feature?

    (Also posted on your other site, but wasn’t sure if you were still monitoring comments over there — sorry if you’ve already read this).



    • Hi Chris,

      Sorry that v17 isn’t working for you. It was designed to be backwards compatible so that it would just replace previous versions in the way you’ve done. Other people have been using v17 successfully, so I’m a bit mystified. I replace v15 with v17 on this page: http://www.dominicsayers.com/isemail/results.php and it works fine.

      Can you give me any more information so I can try to help you?

  11. Hi, Dominic,

    Here’s the include from my form processor:


    and the error check:

    $error.="You entered an invalid e-mail address.";

    Not sure what else I can tell you. I uploaded R17, renamed R15, and gave R17 the same name as the old version. When I hit the submit button, I got a blank page — suggesting the PHP was hanging at the include. Change the names back, & everything works again.

    My original post was here.

  12. Hi, Dominic,

    I just ran R17 thru a PHP syntax checker & it found a couple of comments without the ‘//’ delimiter, near the end of the file. That’s what was tripping things up. R20 is working fine, but you may want to fix any archived copies of R17 still posted out on Google code, or at least include a note in the documentation.

  13. Have anyone tried to write or convert (Somehow) Dominic’s validation code into Java. I am gonna attempt to do that but wanted to know if anyone has done it, as they say “A good programmer is a lazy programmer” :D

Leave a Reply