Learn how to securely send emails in PHP using PHPMailer. This guide covers setup, SMTP configuration, SSL/TLS encryption, and best practices for safe email handling.
You wire up a signup form, drop in mail(), hit send, and it works on your machine. Then you push to production and the “reset your password” emails quietly land in spam, or never arrive at all. If you’ve shipped a PHP app, you’ve probably lived this.
Let’s clear up one thing first, because a lot of tutorials get it wrong: PHP’s mail() function is not deprecated. It still ships in PHP 8, same as it did in PHP 4, 5, and 7. You can call it today and it runs (see the official manual, which lists PHP 8 as supported and carries no deprecation notice).
So this isn’t a “the function is gone, panic” post. It’s the more useful version: mail() is a bad tool for real email, and here’s what to use instead. We’ll set up PHPMailer, send over SMTP with TLS, and close the door on the injection bug that bites people who roll their own.
Why not just use mail()?
Because mail() is a thin wrapper around whatever local mail transfer agent your server happens to have (usually sendmail). That sounds convenient until you need any of the things real email requires.
It has no built-in SMTP support, so you can’t authenticate against a proper mail provider. No auth means worse deliverability, because modern inboxes trust authenticated, aligned senders and distrust mystery mail from a random web host. Its error handling is thin: it returns true when the message was handed off to the MTA, which is not the same as delivered. And if you pass user input straight into the headers without sanitizing it, an attacker can inject extra headers and turn your form into a spam relay. That last one is a real, well-known class of bug called email header injection.
None of that is a deprecation. It’s just mail() being the wrong altitude for the job. A library like PHPMailer gives you SMTP, TLS, sane error handling, and header handling that doesn’t hand attackers the keys.
Setting Up PHPMailer
Step 1: Installing PHPMailer
Install it with Composer, PHP’s dependency manager. If you don’t have Composer yet, grab it from getcomposer.org.
Example:
<?php
composer require phpmailer/phpmailerThat pulls in PHPMailer and its dependencies.
Step 2: Basic Configuration
Now point PHPMailer at an SMTP server. SMTP is how mail actually moves between servers, and sending through an authenticated one is what gets you deliverability instead of a spam folder.
Example:
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'vendor/autoload.php';
$mail = new PHPMailer(true);
try {
//Server settings
$mail->isSMTP(); // Send using SMTP
$mail->Host = 'smtp.example.com'; // Set the SMTP server to send through
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->Username = '[email protected]'; // SMTP username
$mail->Password = 'your_password'; // SMTP password
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // Enable TLS encryption
$mail->Port = 587; // TCP port to connect to
//Recipients
$mail->setFrom('[email protected]', 'Mailer');
$mail->addAddress('[email protected]', 'Recipient Name'); // Add a recipient
// Content
$mail->isHTML(true); // Set email format to HTML
$mail->Subject = 'Here is the subject';
$mail->Body = 'This is the HTML message body in bold!';
$mail->AltBody = 'This is the body in plain text for non-HTML mail clients';
$mail->send();
echo 'Message has been sent';
} catch (Exception $e) {
echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}
Swap the host, username, and password for your provider’s SMTP details. Gmail, your host, a transactional service like a dedicated email API, whatever you use will publish these. One caveat worth stating plainly: don’t hardcode credentials like the example does. Read them from environment variables or a config file that never lands in your repo.
Step 3: Encrypt the connection with TLS or SSL
You want the connection to your mail server encrypted so credentials and message contents don’t travel in the clear. PHPMailer supports both. TLS on port 587 is the usual pick: it opens plain and upgrades to encrypted. SSL on port 465 is the older implicit-TLS style, still fine if your provider prefers it.
Example:
<?php
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // Use TLS
$mail->Port = 587; // TLS port
// Or for SSL
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; // Use SSL
$mail->Port = 465; // SSL portStart with TLS on 587. If your provider only offers SSL, use 465.
Handling Input Safely to Prevent Email Injection
This is the part people skip and regret. If any address, subject, or header comes from a form, treat it as hostile until you’ve validated it. Header injection works by smuggling newlines and extra headers through unsanitized input. Validate first, send second.
Example:
<?php
$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
echo "Invalid email address";
} else {
// Proceed to send email
}PHP’s filter_var() handles the basic case. Good news: when you set recipients and headers through PHPMailer’s methods instead of building header strings by hand, it validates addresses and rejects the newline tricks for you. That’s a big reason to use the library over raw mail(), where you’d be responsible for all of it.
Advanced PHPMailer Configurations
1. Adding Attachments
Attach one file or several with addAttachment().
Example:
<?php
$mail->addAttachment('/path/to/file.pdf'); // Add attachments
$mail->addAttachment('/path/to/image.jpg', 'new.jpg'); // Optional name2. Sending to Multiple Recipients
Call addAddress() once per recipient.
Example:
<?php
$mail->addAddress('[email protected]');
$mail->addAddress('[email protected]');
$mail->addAddress('[email protected]');3. Using HTML Emails
PHPMailer sends rich HTML mail. Include inline CSS, images, and links, and always set a plain-text AltBody too so clients that block HTML still get a readable message.
Example:
<?php
$mail->isHTML(true); // Set email format to HTML
$mail->Body = '<h1>Welcome to Our Service</h1><p>This is an <b>HTML</b> email.</p>';Wrapping up
So, no, mail() wasn’t deprecated, and you don’t have to migrate off it in a hurry. But for anything a real user depends on, a password reset, a receipt, a notification, it’s the wrong tool: no auth, weak deliverability, and injection risk you have to defend by hand.
PHPMailer (or Symfony Mailer, if that fits your stack better) gives you authenticated SMTP, TLS, and safer header handling out of the box. Point it at a proper SMTP provider, keep your credentials out of your code, validate anything a user typed, and your mail will arrive where it’s supposed to.


