Check Email Deliverability in PHP
Before sending en email it is useful to verify whether the email address is valid and can receive emails. This reduces bounce rates and helps in keeping a strong sender reputation. This post demonstrates how to check if an email address is valid in PHP before sending out emails.

Successful email delivery is important to keep a good reputation and bounce rate in control. It ensures every email is delivered to a real user. It is possible to find out if an email address exists by checking MX records of an email address and connecting with mail server to perform an SMTP handshake. In this post we are going to learn how to check if an email address exists and can receive emails. We are going to create following files for this post:
- index.html: The HTML page containing email field form.
- validate-email.php: The server side PHP code to process provided email.
- javascript.js: The JavaScript code to submit form via AJAX.
- style.css: CSS styles for HTML page.
Why Does Email Deliverability and Validation Matter
When email are sent to email boxes that no longer exist or never existed in first place, it increases the bounce rate resulting in a bad sender reputation. To keep a low bounce rate and a good sender reputation, it is important to validate an email address to confirm email deliverability before sending email.
How to Check if an Email Address Exists
It is very easy to check if an email address exists in PHP with simple steps. We can programmatically verify if email is valid in PHP by first validating the email format. Then we check for MX records of domain and attempt a connection with MX servers of email domain. Then finally to check if an email is valid, we perform an SMTP verification before sending an email. To check if email is real or fake via SMTP handshake, we can use following commands:
- HELO: This is the first command which tells the SMTP server a new conversation is about to start.
- MAIL FROM: We tell SMTP server the senders email, the source email address which is going to start conversation.
- RCPT TO: This command tells the SMTP server the target or receiver email address of conversation.
- QUIT: Tells the SMTP server to close conversation.
Lets start with our code implementation by creating user interface to enter an email address, send the form request via AJAX and check if email exists in PHP server-side script.
Step 1: HTML Form with Email Field for Validation
Create an HTML page as user interface. Add an HTML form with email form field and handle form submission in JavaScript to submit form with jQuery AJAX.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Check Email Deliverability in PHP - Demo</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link href="css/style.css" rel="stylesheet" />
<script src="js/jquery-3.1.1.min.js" type="text/javascript"></script>
<script src="js/javascript.js" type="text/javascript"></script>
</head>
<body>
<section class="section py-4">
<div class="container">
<label>Enter an email to validate</label>
<form class="email-form">
<div class="input-group mb-4">
<div class="loader">
<i class="fa fa-spinner fa-spin"></i>
</div>
<input type="email" class="form-control email-input" value="" required="required" placeholder="Enter an email to validate" />
<button type="submit" class="btn btn-green">Validate</button>
</div>
</form>
<div class="content-wrapper" id="content-wrapper"></div>
</div>
</section>
</body>
</html>
Step 2: Submit Email Field Form via AJAX
We need to prevent the default form submission behavior in JavaScript and it via jQuery AJAX to server side PHP code for processing.
javascript.js
$(document).ready(function(){
$(".email-form").on("submit",function(e){
e.preventDefault();
var email = $(".email-input").val();
if(email != ""){
$(".loader").show();
$.ajax({
url: "validate-email.php",
type: "POST",
data:{
email: email,
},
success: function(res){
$(".loader").hide();
$(".content-wrapper").html( '<pre>' + res + '</pre>' );
}
});
}
});
});
Step 3: Programmatically Check Email Deliverability in PHP
We create a PHP script file to verify if an email address valid before sending email. We check for email address format using filter_var() function, we then check the MX records of email and finally to check if the email exists, we perform an SMTP handshake. To check if an email is real or fake we follow these steps:
- Set
UNKNOWN_ERRORas initial response. - Check if form data was submitted and
$_POSTvariable is not empty. - Check if email was sent in form data.
- Check if email address is in valid email format using the
filter_var()function. - Get MX records of email domain using
getmxrr()function. - Open a connection with MX server for each MX record.
- Send SMTP commands to mail server using
fwrite()function for SMTP handshake and capture server response usingfgets()function. The response code 250 means email address exists. - Finally return the response in pretty JSON format.
validate-email.php
<?php
// Initial response as a fallback
$res['success'] = 0;
$res['status'] = 'UNKNOWN_ERROR';
if(!empty($_POST)){
// Check if email was submitted
if(!isset($_POST['email'])){
$res['status'] = 'MISSING_EMAIL';
}
$email = $_POST['email'];
// Check if email format
$valid_format = filter_var($email, FILTER_VALIDATE_EMAIL);
// If email format is correct proceed to validation check
if($valid_format){
// Get hostname from email
$hostname = substr($email, strpos($email, '@') + 1);
$mxhosts = $mxweights = [];
// Get MX-Records of hostname
$mxr = getmxrr($hostname, $mxhosts, $mxweights);
$mxr_list = array_combine($mxweights, $mxhosts);
// If MX-Records were found proceed to socket connection
if( !empty($mxr_list) ){
// Sort MX-Records by priority
krsort($mxr_list, SORT_NUMERIC);
$res['status'] = 'INVALID_EMAIL';
// Loop through all MX-Records
foreach($mxr_list as $mxweight => $mxhost){
$connection = @fsockopen($mxhost, 25, $errno, $errstr, 5);
// If connection was successful send commands to email server
if($connection){
// Send Helo command to initiate conversation
fwrite($connection, "HELO $mxhost\r\n");
$response = fgets($connection, 1024) . "\r\n";
// Send Mail From command to specify senders email
fwrite($connection, "MAIL FROM: <[email protected]> \r\n");
$response = fgets($connection, 1024) . "\r\n";
// Send RCPT TO command to specify receiver's email on server
fwrite($connection, "RCPT TO: <$email> \r\n");
$response = fgets($connection, 1024) . "\r\n";
// Send QUIT command to close conversation
fwrite($connection, "QUIT \r\n");
$response = fgets($connection, 1024);
fclose($connection);
// If last response returned 250 its a valid email
if( substr($response, 0, 3) == 250 ){
$res['success'] = 1;
$res['status'] = 'EMAIL_VALID';
$res['mx_hosts'] = $mxr_list;
break;
}
}
}
}else{
// No MX-Records found
$res['status'] = 'EMAIL_SERVER_NOT_FOUND';
}
}else{
// Invalid email format
$res['status'] = 'INVALID_FORMAT';
}
}
echo json_encode($res, JSON_PRETTY_PRINT);
Step 4: Add HTML & Form CSS Styles
Add CSS styles for entire HTML page, email address field and response container sent from server.
style.css
* {
box-sizing: border-box;
}
html,body {
margin: 0;
padding: 0;
}
body {
background-color: #f6f6f6;
font-family: "Segoe UI", "Roboto", "Helvetica", sans-serif;
font-size: 15px;
font-weight: normal;
font-style: normal;
}
.container {
width: 100%;
max-width: 1140px;
margin-right: auto;
margin-left: auto;
padding-right: 15px;
padding-left: 15px;
}
.inline-block {
display: inline-block;
}
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.input-group {
position: relative;
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.form-control {
display: block;
width: 100%;
padding: .375rem .75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #3b3b3b;
background-color: #ffffff;
background-clip: padding-box;
border: 1px solid #d1d2d3;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
.input-group > .form-control {
position: relative;
flex: 1 1 auto;
width: 1%;
min-width: 0;
}
.btn {
display: inline-block;
padding: 5px 10px;
cursor: pointer;
font: inherit;
}
.btn-green {
background-color: #319764;
border: 1px solid #248A57;
color: #ffffff;
}
.input-group .btn {
position: relative;
z-index: 2;
margin-left: -1px;
}
.loader {
position: absolute;
font-size: 25px;
background: rgba(150,150,150,0.5);
width: 100%;
height: 100%;
z-index: 5;
padding: 0 10px;
display: none;
color: #006699;
text-align: center;
}
.content-wrapper pre {
background-color: #fff;
border: 1px solid #ddd;
padding: 15px;
}