SMTP Email Address Validation

How do you validate an email address? You may validate it with a javascript on the client-side, or run a server side validation via a regular expression or a newly available filter_var function of the PHP 5.2.x. Perhaps, you may want to go one step further, and verify the email address from the remote SMTP server.

Generally, a server-side email address validation would be suffice for most application. If you do have to run a SMTP based email address verification, the following script will help you do that. This is a modified version of the PHP SMTP Email Validation from Google Project.

class SmtpValidator {
	private $options = array(
			"port" => 25,
			"timeout" => 1,  // Connection timeout to remote mail server.
			"sender" => "[email protected]",
			"short_response" => false,
	 *  Override the options for those specified.
	function __construct($options = null) {
		if (!empty($options)) {
			if (is_array($options)) {
				foreach ($options as $key => $value) {
					$this->options[$key] = $value;
	 *  Validate the email address via SMTP.
	 *  If 'shore_response' is true, the method will return true or false;
	 *  Otherwise, the entire array of useful information will be provided.
	public function validate($email, $options = null) {
		$result = array("valid" => false);
		$errors = array();
		// Email address (format) validation
		if (empty($email)) {
			$errors = array("Email address is required.\n");
		} else if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
			$errors = array("Invalid email address.\n");
		} else {
			list($username, $hostname) = split('@', $email);
			if (function_exists('getmxrr')) {
				if (getmxrr($hostname, $mxhosts, $mxweights)) {
					$result['mx_records'] = array_combine($mxhosts, $mxweights);
				} else {
					$errors = "No MX record found.";
			foreach ($mxhosts as $host) {
				$fp = @fsockopen($host, $this->options['port'], $errno, $errstr, 
				if ($fp) {
					$data = fgets($fp);
					$code = substr($data, 0, 3);
					if($code == '220') {
						$sender_domain = split('@', $this->options['sender']);
						fwrite($fp, "HELO {$sender_domain}\r\n");
						fread($fp, 4096);
						fwrite($fp, "MAIL FROM: <{$this->options['sender']}>\r\n");
						fwrite($fp, "RCPT TO:<{$email}>\r\n");
						$data = fgets($fp);
						$code = substr($data, 0, 3);
						$result['response'] = array("code" => $code, "data" => $data);
						fwrite($fp, "quit\r\n");
						switch ($code) {
							case "250":  // We're good, so exit out of foreach loop
							case "421":  // Too many SMTP connections
							case "450":
							case "451":  // Graylisted
							case "452":
								$result['valid'] = true;
								break 2;  // Assume 4xx return code is valid.
								$errors[] = "({$host}) RCPT TO: {$code}: {$data}\n";
					} else {
						$errors[] = "MTA Error: (Stream: {$data})\n";
				} else {
					$errors[] = "{$errno}: $errstr\n";
		if (!empty($errors)) {
			$result['errors'] = $errors;
		return ($this->options['short_response']) ? $result['valid'] : $result;

Although the script has been disclosed in the article, using the SMTP to verify the email address is somewhat dangerous for three reasons: (1) Not all MTAs will have the VRFY command turned on to enable this functionality, (2) Some MTAs may return incorrect status code due to server busy or graylist reasons, and (3) Sending multiple "RCPT TO" request to a same MTA may result in enrollment into a blacklist.



Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.