Check Email Deliverability in PHP
It is important to know if an email address is a valid and it can receive emails before sending any email to that address. This helps to reduce the bounce rate and keep a good sender reputation. Learn how to check if an email address is valid in PHP before sending emails.

Successful email delivery is important to maintain a good sender reputation and bounce rate under control. It is possible to find out if an email address exists. We can do so by checking the MX records of the email address and connecting to the email server and perform an SMTP handshake. This post demonstrates how to check if an email address exists and can receive emails. We will need to create following files to demonstrate it:
- index.html: An HTML page with an email field.
- validate-email.php: A server-side PHP script to validate an email address.
- javascript.js: A client-side JavaScript to submit the form with AJAX.
- style.css: CSS styles for the HTML page.
Why Is Email Deliverability and Validation Important?
When emails are sent to email boxes that no longer exist or never existed in the first place, it increases the bounce rate, resulting in a bad sender reputation. It is important to validate an email address to confirm email deliverability.
How to Check if an Email Address Exists?
Finding out if an email exists or not is easy with combination of a few steps. First we validate the email format then we check for the MX records of the domain and attempt a connection with the MX servers of the email domain. Then we perform an SMTP verification before sending an email. To check if an email is real or fake via SMTP handshake, we can use the following commands:
- HELO: This is the first command that tells the SMTP server a new conversation is about to start.
- MAIL FROM: We tell the SMTP server the sender's email, the source email address that is going to start a conversation.
- RCPT TO: This command tells the SMTP server the receiver email address of a conversation.
- QUIT: Tells the SMTP server to close a conversation.
We can start implementing it by creating a user interface to enter an email address. Then we send the form request with an AJAX request. Then we check if an email exists in a server-side PHP script.
Step 1: HTML Form with Email Field for Validation
First step is to create an HTML page as a user interface with an email input field. We prevent the default form submission behavior and submit the form with jQuery AJAX in JavaScript.
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 rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
<link rel="stylesheet" href="css/style.css" />
<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 submit it via jQuery AJAX to the server-side PHP code for processing.
javascript.js
$(document).ready(function () {
$(".email-form").on("submit", function (e) {
e.preventDefault();
const 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: Check Email Deliverability in PHP
We create a PHP file that is responsible to verify if an email address is valid before sending an email. We check for email address format using the filter_var() function. We then check the MX records of the email, and finally, to check if the email exists or not, we perform an SMTP handshake. To check if an email is real or fake, we follow these steps:
- Set
UNKNOWN_ERRORas an initial response. - Check if form data was submitted and the
$_POSTvariable is not empty. - Check if the email was sent in form data.
- Check if the email address is in a valid email format using the
filter_var()function. - Get MX records of the email domain using the
getmxrr()function. - Open a connection with the MX server for each MX record.
- Send SMTP commands to the mail server using the
fwrite()function for SMTP handshake and capture server response using thefgets()function. The response code 250 means an email address exists. - Finally, return the response in a 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 the entire HTML page, email address field, and response container sent from the 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: #ffffff;
border: 1px solid #dddddd;
padding: 15px;
}Thats it! We just created an email verification system that not only validates an email address but also checks its existence. We can identify an invalid email address by validating it's format and MX records. Whereas performing an SMTP handshake ensures users enter the email address they actually have access to.