Validate an E-Mail Address with PHP, the Right Way

Develop a working PHP function to validate e-mail addresses.

The Internet Engineering Task Force (IETF) document, RFC 3696, “Application Techniques for Checking and Transformation of Names” by John Klensin, gives several valid e-mail addresses that are rejected by many PHP validation routines. The addresses: Abc\, customer/ and !def! are all valid. One of the more popular regular expressions found in the literature rejects all of them:


This regular expression allows only the underscore (_) and hyphen (-) characters, numbers and lowercase alphabetic characters. Even assuming a preprocessing step that converts uppercase alphabetic characters to lowercase, the expression rejects addresses with valid characters, such as the slash (/), equal sign (=), exclamation point (!) and percent (%). The expression also requires that the highest-level domain component has only two or three characters, thus rejecting valid domains, such as .museum.

Another favorite regular expression solution is the following:


This regular expression rejects all the valid examples in the preceding paragraph. It does have the grace to allow uppercase alphabetic characters, and it doesn't make the error of assuming a high-level domain name has only two or three characters. It allows invalid domain names, such as

Listing 1 shows an example from PHP Dev Shed ( The code contains (at least) three errors. First, it fails to recognize many valid e-mail address characters, such as percent (%). Second, it splits the e-mail address into user name and domain parts at the at sign (@). E-mail addresses that contain a quoted at sign, such as Abc\ will break this code. Third, it fails to check for host address DNS records. Hosts with a type A DNS entry will accept e-mail and may not necessarily publish a type MX entry. I'm not picking on the author at PHP Dev Shed. More than 100 reviewers gave this a four-out-of-five-star rating.

One of the better solutions comes from Dave Child's blog at ILoveJackDaniel's (, shown in Listing 2 ( Not only does Dave love good-old American whiskey, he also did some homework, read RFC 2822 and recognized the true range of characters valid in an e-mail user name. About 50 people have commented on this solution at the site, including a few corrections that have been incorporated into the original solution. The only major flaw in the code collectively developed at ILoveJackDaniel's is that it fails to allow for quoted characters, such as \@, in the user name. It will reject an address with more than one at sign, so that it does not get tripped up splitting the user name and domain parts using explode("@", $email). A subjective criticism is that the code expends a lot of effort checking the length of each component of the domain portion—effort better spent simply trying a domain lookup. Others might appreciate the due diligence paid to checking the domain before executing a DNS lookup on the network.



Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Simple & effective

Objektive Mieten's picture

Simple & effective:

if(!filter_var($email, FILTER_VALIDATE_EMAIL))
exit("E-mail is not valid");


voiture sans permis's picture

If you want a PHP script to verify an email address then use this quick and simple PHP regular expression for email validation. This is also case-insensitive, so it will treat all characters as lower case. It is a really easy way to check the syntax and format of an email address

There are a lot short

Mike Crowl's picture

There are a lot short description like this one, but usually application requires a lot validation and need have some common solution. And it will be good to have easy way to add this solution to ready/live project. For now I found Vitana validation solution only . It is good but I prefer to have a choice. Can anybody provide there link to another good validation functionality.

This article made my cry a river

Anonymous's picture

it doesn't even allow a gmail email address.

RFC 3696 has errata. This article is WRONG!

dominicsayers's picture

I can't believe LJ have allowed this article to remain here uncorrected for so long.

The examples given in this article are NOT VALID. John Klensin has corrected the RFC but you would only know this if you click the Errata link

I know of nothing that Doug Lovell or LJ have done to correct the misinformation on this page.

More information here:


Michael Rushton's picture

It might also be worth noting, for those who don't know, that PHP has an in-built function for validating email addresses:

filter_var($email_address, FILTER_VALIDATE_EMAIL)


Oleg Gerasimenko's picture

filter_var($email_address, FILTER_VALIDATE_EMAIL) doesn't cover all RFC specifications, for example, umlauts.

Umlauts are not allowed.

Michael Rushton's picture

Umlauts are not allowed.


icemanza's picture


What are the merits of adding an fsockopen check on the domain as an additionla test at the end?


how can this be added to an

Anonymous's picture

how can this be added to an existing contact form/ contact php script?

Check if an email address is from a valid domain

Anonymous's picture

Wanted, but not needed is a module that would function sort of like a MySpace area for each user. Pictures, Videos, Blog, profiles, maybe some games, etc...

This function is great but

Saint's picture

This function is great but when I delete the DNS check it accept emails like something@something where the domain doesn't contain any "."

//PS. sorry for my english :P

Anonymous's picture

Hi thanks very much that ! Just one question - how to fix it to accept email adressess ?

Thanks in advance!

function should work fine

Anonymous's picture

function should work fine with addresses. As long as you give it something like it will work fine. It just can't have a double dot, or start with a dot. So will fail because it isn't a properly formatted domain.

Validate an E-Mail Address with PHP, the Right Way

Anonymous's picture

Thanks for providing this article. So many google searches only produce "this works for me in my environment/work" without any explanation of how it does it's magic. This leads to others posting direct or modified copies, leading to errors being propagated.
On reading the comments one wonders if some actually read the full article.
I've little experience with regular expressions but it seems logical to me to search for the presence of the six or seven not allowed characters rather than for the non-presence of the other 120 odd character codes.
I will now soak myself in the explanation. is not passing - it doesn't like the dash

Anonymous's picture


first thank you for this contribution. Finally someone who takes care of RFCs ;)

Now I am being not very good with the regex but when I test it wiht a domain and a dash in it, it doesn't work.

At this time I don't know what to change of this code:

else if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain))(/code)

so if there is one person who can point out what to change this would be really appreciated.



Michael Rushton's picture

Try this:


Obviously there are problems with this; it allows consecutive dashes and periods, as well as allowing dashes and periods both next to each other and at the start and the end, and also allowing a TLD that starts with a number.

Without the slash before the

Michael Rushton's picture

Without the slash before the dot, of course...


It looks like that particular

Anonymous's picture

It looks like that particular regular expression is broken. It allows backslashes in the domain name. The author forgot that this is a character class inside a single-tick PHP string and the rules that apply to the rest of a regular expression don't apply here. The part of the string '\\-\\' appears to be intended as an escaped backslash. However, PHP treats backslashes inside single-tick (') PHP strings differently from quoted strings ("). It actually doesn't do any escaping. So, the end result is the range of characters '\-\'. Now, the regular expression engine on my local PHP install appears to see that and then probably assumes the author screwed something up and allows '\' and '-'. However, it is also entirely possible on other PHP versions, such as yours, that the regular expression engine sees that as only allowing '\' characters.

That's the long-winded explanation. Now for the "solution"*:

else if (!preg_match('/^[A-Za-z0-9\-.]+$/', $domain))

Inside a character class, the '.' character is treated as a real '.' character. It does not need to be escaped. However, the '-' character is used for ranges of characters (e.g. 'A-Z'), so it has to be escaped.

* In general, regular expressions are used INCORRECTLY and are almost always broken in some fashion. Any time I see a preg_match(), I immediately assume that the code is broken and 99% of the time I'm right. Only preg_replace() is correct. Regular expressions are NOT for data validation but data FILTERING. preg_replace() is the only PHP function that meets that criteria. Barely. I still cringe when I see even that. So, the "solution" above is still technically broken. All I did was remove the weird issue documented at the start of my reply.

I see this and every other e-mail address validation routine as completely broken. Those with domain validation won't work on most Windows servers (checkdnsrr() is only available on Windows as of 5.3.x) and they all use preg_match() instead of preg_replace().

A good "validation" routine should clean up clearly broken addresses instead of just declaring them bad. Detecting a bad domain is easy - attempt to get the MX or A record for it (PEAR Net::DNS should work everywhere) and the number of characters in a domain is very limited. Detecting bad characters before the '@' is best left to the destination mail server. You can do some _obvious_ filtering but, other than that, just assume it will work.

Also, it appears that the maximum length of an e-mail address is 254 characters:

And near the bottom of that page, you can see that it appears the author there doesn't like this function either!

Of course, his function is only slightly better than yours - it too uses preg_match() and getdnsrr().

I once saw a regular expression to supposedly validate ALL e-mail addresses that was several pages long from some O'Reilly book. That alone should tell you that e-mail validation is HARD to IMPOSSIBLE. E-mail filtering, however, should be magnitudes easier.

I realize this must be hard to swallow that all these years people have been doing it all wrong, including yourself. But don't feel too bad! Everyone doing validation is in a similar-looking boat. They look similar because the boats completely missed the water!

Clarification and comments

Davnor's picture

The correction offered by "Anonymous" will work, but his/her explanation is a little off the mark.

The only actual problem with the original expression is that it incorrectly escapes the period inside the character class.

However, the issue of escaping a backslash inside a single-quoted string is entirely separate from the issue of escaping characters inside a regex character class. The evaluation of the escaped backslashes is performed by PHP's string processor, BEFORE the string is ever passed to the regex engine.

For example, consider the following two strings (not regular expressions):

'\\this is a backslash'
'\this is a backslash'

Both evaluate to:

\this is a backslash

However, it is still good practice to escape backslashes in single quoted strings, to avoid errors later. For example, let's say we decide to add some escaped single-quotes to the examples above:

'\\\'this is a backslash\''
'\\'this is a backslash\''

The second string now contains a syntax error, because the first backslash is now treated as the beginning of an escape sequence, instead of a literal backslash.

So, back to the regular expression. The original single quoted-string,


gets resolved to the expression,


which is then passed to the regex engine. Note that the dash is properly escaped, but the period is escaped too, which is not correct. So, we just need to take out the backslashes before the period,


so it now resolves to,


which allows dashes and periods, but not backslashes, which is what we want. :-)

Again, the solution offered by Anonymous will work too, it's just not following the best practice, in my opinion.

Another option is put the dash at the end of the character class, which will cause the regex engine to treat it as a literal, so then nothing has to be escaped:


On a separate note, I take exception to Anonymous's generalizations about the use of regular expressions. It is true, the subtleties of regular expressions can certainly trip up programmers, but I think it's unrealistic to automatically assume that most of the expressions in use are incorrect.

I am also genuinely baffled by this assertion:

"Regular expressions are NOT for data validation but data FILTERING."

Where did this idea come from? If you want to be precise, regular expressions are not for filtering either; they are designed for one purpose: pattern matching.

But what good does it do to match (or not match) a pattern if you can't do anything useful with that information. Both preg_match() and preg_replace() use a regular expression in the same way - they look for matching pattern(s); the only difference is what the functions do after a match is found or not found.

As far as performing validation, this simply means "check if the data is valid". But then you have to define what is valid in a given context. In this article, a valid email address is defined as follows:

  1. It meets the formal standard for the format of an email address.
  2. The domain part of the address can be verified by a DNS lookup.

To me, that seems like a pretty reasonable definition of a valid email address. And, since criterion (1) involves matching patterns, regular expressions are the perfect tool for the job.

At least, those are my two cents. [Stepping off of soapbox]

Spurious Character

Anonymous's picture

Sorry if this seems like a dumb question, but is that weird character -- immediately to the left of the second checkdnsrr reference -- spurious or intended? Only when I paste the code into my code editor it's just showing up as a square box (like: □).

It's a right hooked arrow

Mitch Frazier's picture

That's a "↪" HTML entity. It should look like:


Those are used in the print magazine (which is where this was originally published) to indicate that the line was wrapped during print layout because it was too long.

If you're not seeing that in your browser then you may have the character encoding set incorrectly. Try setting it to UTF-8.

Mitch Frazier is an Associate Editor for Linux Journal.

I am having the same problem.

Anonymous's picture

I am having the same problem. I am a newbie and do not now how to change the character setting to UTF-8. Can you please show me how to do this.

Thank you.


Mitch Frazier's picture

What browser are you using?

Mitch Frazier is an Associate Editor for Linux Journal.

Very cool

Anonymous's picture

This is an excellent article and filled alot of holes in my knowledge of email validation. Thank you so much for this



Anonymous's picture

for some unknown reasons the used checkdnsrr() returns false positives on some systems even for clearly unexisting domains (for example etc /syntax ok but does not exist/) so I recommend changing that part to get_dns_record followed by count() like this :

// here some basic rules check and local part check
// before we try querying dns

if ($isValid===true) // if still ok
{ $records = get_dns_records($domain);
if (is_array($records) AND count($records)>0) // domain found in DNS
{ $isValid = true;}
else {$isValid = false;}
return $isValid

(imho this maybe be a little more hungry on high-load servers,
but as the get_dns_records() will not find any records for unexisting domains, this should be very reliable check)


Always add a full stop to the

Anonymous's picture

Always add a full stop to the end of the domain, otherwise the server tries to make the domain valid by appending its own domain name.

Thank You & Topic Suggestion

Anonymous's picture

Thank you for your quality journal piece and explanation of all the considerations that really go into this issue. Very, very helpful.

Through most of 2009 I was tasked weekly to fix websites that had gone Viral ( in the bad way ).

Cause: JavaScript and SQL injection to weak forms. AND previous programmer teams who spent too much time downloading videos and games, often infecting their computers with viruses that would actually "read their FTP" files and/or "snoop FTP" logins.
Effect: All sites they ever worked on, show up as malware downloader sites overnight.

That's when I get "The Call"... usually at midnight, yeah?

This year... I've had to fix dozens of sites whose forms were getting "pounded" by form spammer bots. 80 to 1000 emails per day per site.

I will be applying your "Complete Example" above to a new set of sites and landing pages that have to go live this week, ready to accept traffic from AdWords. I want to stop the "Form Input Spam" bots and the "JavaScript Field Injection" Form Violator/Malware bots in their tracks ahead of time. While, of course, making sure that real leads get through, properly formatted!

Next I will be searching for: Phone Number entry format validators, a JavaScrip field Injection Stoppers, and pre-submit Human Tests ( ie Captcha ). All of which I wish to wrap up into a single PHP+JS library I can re-use for all of my upcoming sites.

My I suggest aanother article covering this "complete set" of form-related considerations as a full-scope topic, for website builders such as myself.

Thanks again,

James Arvigo
Website Admin

Syntax question

Anonymous's picture

I was wondering what the ? operator placed directly before a function meant. It wasn't used in the if-else ? : way so it's confusing me. The line of code I'm looking at is where he uses checkdnsrr() for the second time near the bottom of the code. I copied and pasted and got ?checkdnsrr($domain,"A") I tried running the code and got... Oh, wait. I wonder if checkdnsrr doesn't work with my version of Windows. But I'm at XP right now and I thought it was older versions that it didn't work with. Well, I shall be back if my OS isn't the problem.

*scratches head*

Anonymous's picture

Ok, so checkdnsrr was only implemented at php 5.3.0 which I am running.

Great stuff

Anonymous's picture

I can't seem to use the checkdnsrr function in my local version but other than that it's great. If you can reply - to pieter .at. cheerful .dot. .com. , I didn't think that 'me@home' was a valid email address?

Thank you

Checking Mail box validity

Anonymous's picture

Hi All,

Good Morning,

function checkEmail($email) {
echo $email;
if(preg_match("/^([a-zA-Z0-9])+([a-zA-Z0-9\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/" , $email))

if(!getmxrr ($domain,$mxhosts)) {
return false;
return true;
return false;

The Above code checks the validity of the email and also returns if
the domain exists. But I need to check the validity of the mail box

Example: Mail Address) (Valid Mail Address) though the mail box doesn't not exists
can we validate the mail box exsistency....... Help me.


Are emails like

Anonymous's picture

Are emails like somelocal@.domain are valid? What about dot at the begining of domain name? preg_match('/^[A-Za-z0-9\\-\\.]+$/' is not enough I guess...

Modified regexp

Anonymous's picture

Hi, I modified the "favorite regular expression" to :


Actually, it now checks if the domain part begins with a letter and checks the extension (2 letters or more - I had many adresses ending with .f or .).

email testing list

Anonymous's picture

Hi, I was going through the list of emails given to test this validation function and when I entered abc\\ the third email address down on the list of emails that should not be valid, it said it was valid and sent the email.

Is there something wrong? should this email address validate? Is there an error in the list of emails that should not pass validation?

just wondering :o/

where is the right address for this path http://www.actionscript

Nicolas's picture

Where is the right address for this path?


A not about TLD's

Anonymous's picture

If you have added some non dns based TLD (top level Domain) checking - beware! It is highly probable that in the near future TLD's will be opened up and could get quite long.

email addresses like: myname@thisisareallylongtld could be valid, and under the spec they are legal (note the lack of .'s).

Assuming that all tld's will be 6 chars or less, just because that is presently true is short sighted. I have seen people use email checkers with an internal list of TLD's to compare to, and they have been frustrated out how out of date such lists become.

Email addresses like;

Could potentially be used in the future - While DNS may be expensive, if you want to be certain that the domain is valid, DNS is the only way this can be achieved.

Hey, great article, but the

John O'Connell's picture

Hey, great article, but the link to IloveJackDaniels is no longer valid. His site has been moved to

Didn't know some of the characters you showed were valid.. Thanks for the tips!

RFC 5322 email-validation regex

Michael Rushton's picture

Your code doesn't seem to disallow consecutive hyphens in the domain name, which it should (unless internationalized, in why case the domain address may begin: xn--).

The following regex, I believe, complies completely with RFC 5322. And also does away with the need for any further functions (save for checking to see if the domain name actually exists):


Another (and hopefully final) update

Michael Rushton's picture

After looking over my regex, I decided it could do with some tidying up. Here is the cleaner version, included in a function:

function email($email) {
return preg_match("/^(?!.{255,})(?!.{65,}@)(?:(?:\"[^\"]{1,62}\")|(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*))@(?:(?:\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9]{1,2})\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9]{1,2})\])|(?:(?!.*[^.]{64,})(?:(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*\.){1,127}(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*))$/i", $email);


Michael Rushton's picture

I've noticed a small error in my regex. It disallowed an address of the form, which is legal. Here is the update:


Not valid regex?

Anonymous's picture

I might doing something stupidly wrong but plugging your regex into PHP5.2 and using Douglas's test list, it rejects these that should pass

abc\ is int(0)
abc\\ is int(0)
Fred\ is int(0)
Joe.\\ is int(0)
Doug\ \"Ace\"\ is int(0)
"Doug \"Ace\" L." is int(0)

The code used
$isValid=preg_match("/^(?=.{5,254})(?:(?:\"[^\"]{1,62}\")|(?:(?!\.)(?!.*\.[.@])[a-z0-9!#$%&'*+\/=?^_`{|}~^.-]{1,64}))@(?:(?:\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\])|(?:(?!-)(?!.*-\$)(?!.*-\.)(?!.*\.-)(?!.*[^n]--)(?!.*[^x]n--)(?!n--)(?!.*[^.]xn--)(?:[a-z0-9-]{1,63}\.){1,127}(?:[a-z0-9-]{1,63})))$/i", $email);

While my above regex is

Michael Rushton's picture

While my above regex is actually quite lacking (the updated version is available on my website, which I'm hoping will be linked to via my name or somewhere in this post), those email addresses are obsoleted by RFC 5322. True, they ought to be accepted as valid, but only for backward compatibility's sake. My validation was intended to allow for semantically-visible (no comments or folding white space) and non-obsolete email addresses.


Michael Rushton's picture

Note: I meant to say that each LABEL of the domain name may begin with xn-- if internationalized.

Another Else If

Nate D's picture

I added another else if to the test because i noticed that bob@yahoo would pass. So I added a check to see if there is a dot 4 positions before the end of the domain section. View below:

else if(substr($domain,($domainLen - 4),1) != ".")
//no dot 4 positions before the end
$isValid = false;

make it check this twice one

Anonymous's picture

make it check this twice one time for position 2 then 3.


Anonymous's picture

Whatabout .nl, .ca, .ru, .more or less anything? .com is way too limited I'd guess

It seems to have the grace to

greggy's picture

It seems to have the grace to allow uppercase alphabetic characters, and it doesn't make the error of assuming a high-level domain name has only two or three characters.
meizitang botanical

What do you think about it?

Aravak's picture

What do you think about filter_var function? It's standart php function for validate data.
You can see example in my blog - Email validation without regexp.

In Russian Проверка Email на php.

need to update the link to ilovejackdaniels

Anonymous's picture will die soon, the new link is here: