PHP Mail & SMTP
Also Known As
TL;DR
Explanation
PHP's built-in mail() function is a thin wrapper around the server's sendmail binary. It works on correctly configured servers but has no SMTP authentication, no TLS support, no error handling beyond a boolean return, and no queue — if the mail server is unavailable, the email is silently lost. For production, PHPMailer or Symfony Mailer send via authenticated SMTP with TLS, provide proper error exceptions, and support HTML email with embedded images. Transactional email APIs (Mailgun, SendGrid, Amazon SES, Postmark) provide delivery tracking, bounce handling, and high deliverability without managing an SMTP server. Deliverability requires three DNS records: SPF (which servers may send on your behalf), DKIM (cryptographic signature proving origin), and DMARC (policy for failed SPF/DKIM). Missing these records causes emails to land in spam or be rejected silently.
Common Misconception
Why It Matters
Common Mistakes
- Using mail() in production without SPF, DKIM, and DMARC DNS records — emails go to spam or are rejected.
- Not using a queue for email sending — synchronous email blocks the HTTP response and fails silently if the SMTP server is slow.
- Not validating email addresses with filter_var($email, FILTER_VALIDATE_EMAIL) before sending.
- Exposing SMTP credentials in version-controlled config files — always use environment variables for SMTP passwords and API keys.
Code Examples
// mail() — no error handling, no TLS, poor deliverability
$sent = mail(
$_POST['email'], // unvalidated
'Welcome',
'Thanks for signing up',
'From: noreply@example.com' // likely to be spam
);
// Returns true even if email never delivered
// PHPMailer with SMTP + TLS
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
$mail = new PHPMailer(true); // true = throw exceptions
try {
$mail->isSMTP();
$mail->Host = $_ENV['SMTP_HOST'];
$mail->SMTPAuth = true;
$mail->Username = $_ENV['SMTP_USER'];
$mail->Password = $_ENV['SMTP_PASS'];
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->setFrom('noreply@example.com', 'My App');
$mail->addAddress(filter_var($email, FILTER_VALIDATE_EMAIL));
$mail->Subject = 'Welcome';
$mail->Body = 'Thanks for signing up';
$mail->send();
} catch (Exception $e) {
logger()->error('Mail failed: ' . $e->getMessage());
}