PHPMailer is the most widely used library for sending email from PHP. It has been in use since 2001, ships in WordPress, Drupal, Joomla and thousands of applications, and is installed millions of times a month through Composer. If a PHP application sends mail, there is a good chance PHPMailer is doing it.
This domain hosted the original PHPMailer website and its documentation for over a decade. Development moved to GitHub years ago: github.com/PHPMailer/PHPMailer is the authoritative source for releases, issues and security advisories. The pages here are tutorials, worked examples and a reference for using the library day to day.
What it actually does for you
PHP has a built-in mail() function, so the reasonable question is why a library exists at all. Because mail() gives you a string and a hope:
mail() |
PHPMailer | |
|---|---|---|
| SMTP with authentication | No | Yes |
| TLS / STARTTLS | No | Yes |
| Error reporting | A boolean | Server response in ErrorInfo, or an exception |
| MIME multipart | Build the headers yourself | Handled |
| Attachments | Manual base64 and boundaries | addAttachment() |
| Inline images | Manual cid: parts |
addEmbeddedImage() |
| HTML + plain-text alternative | Manual | isHTML() and AltBody |
| Non-ASCII subjects | Manual encoding | CharSet |
| DKIM signing | No | Built in |
| Header-injection protection | Your problem | Validated |
That last row is not a convenience. Passing unvalidated user input into mail headers is a real vulnerability class — an attacker who can inject a newline can add recipients and turn your contact form into an open relay. PHPMailer rejects those values instead.
Sending an email, minimally
composer require phpmailer/phpmailer
<?php
use PHPMailerPHPMailerPHPMailer;
use PHPMailerPHPMailerException;
require 'vendor/autoload.php';
$mail = new PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = 'smtp.example.com';
$mail->SMTPAuth = true;
$mail->Username = getenv('SMTP_USER');
$mail->Password = getenv('SMTP_PASS');
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->setFrom('noreply@example.com', 'Your App');
$mail->addAddress('recipient@example.com');
$mail->Subject = 'Hello';
$mail->Body = 'Sent with PHPMailer.';
$mail->send();
} catch (Exception $e) {
echo 'Failed: ' . $mail->ErrorInfo;
}
The true in the constructor turns on exceptions, and it is the single most useful thing on this page. Without it, send() returns false and it is on you to check — which is how applications quietly stop sending email for weeks before anyone notices.
Documentation
Getting started
- Installing PHPMailer — Composer and manual installation, required extensions, upgrading from 5.x.
- Tutorial — step by step from an empty project to authenticated SMTP with HTML and attachments.
Examples
- All examples — one working script per transport:
mail(), sendmail, SMTP, Gmail App Password, Gmail XOAUTH2, batching. - SMTP example — full configuration, port and encryption pairs, every error explained, provider settings.
- mail() example — the local transport, its one serious limitation, and when it is still fine.
Reference
- Methods reference — signatures and behaviour for the methods you will call.
- Properties reference — every configurable property with type and default.
- Extending PHPMailer — subclassing so configuration lives in one place.
The three things that go wrong
Almost every PHPMailer problem is one of these.
SMTP connect() failed
The connection never opened, so credentials were never even tried. Usually the port and encryption constant disagree — 587 goes with ENCRYPTION_STARTTLS, 465 with ENCRYPTION_SMTPS, and crossing them fails every time. Otherwise it is an outbound firewall; most shared hosts and cloud providers block SMTP by default. Check reachability before debugging PHP:
openssl s_client -starttls smtp -connect smtp.example.com:587
Could not authenticate
The connection worked, the credentials were refused. On Gmail this is nearly always an account password used where an App Password is required. Elsewhere, check whether the username should be the full email address or just the local part — providers differ and both are common.
It sends, but everything lands in spam
Not a PHPMailer problem at all, and no code change will fix it. Acceptance is decided by DNS records for your sending domain:
- SPF — a TXT record listing which servers may send for the domain.
- DKIM — a public key so receivers can verify the message was signed by you and not altered in transit.
- DMARC — a policy telling receivers what to do when SPF and DKIM fail, and where to send reports.
Missing DKIM is the usual reason a technically perfect message gets filtered. If your host does not sign outgoing mail, PHPMailer will:
$mail->DKIM_domain = 'example.com';
$mail->DKIM_selector = 'phpmailer';
$mail->DKIM_private = '/secure/path/dkim_private.pem';
$mail->DKIM_identity = $mail->From;
When something fails, turn on the protocol conversation before guessing. It identifies the cause on the first run in almost every case:
use PHPMailerPHPMailerSMTP;
$mail->SMTPDebug = SMTP::DEBUG_SERVER;
Versions
6.x is the current line. It is namespaced (PHPMailerPHPMailerPHPMailer), installed with Composer, and supports PHP 5.5 upward including current releases.
5.x — the era of require 'class.phpmailer.php' and a global PHPMailer class — reached end of life years ago and receives no security fixes. Code written against it will not run on 6.x unchanged, though the changes are small; the installation page has the mapping.
PHPMailer or something else?
If you are already inside a framework, use its mailer. Symfony Mailer and Laravel’s Mail facade are wired into the container, the queue and the template engine, and you gain nothing by bypassing that.
PHPMailer suits standalone scripts, legacy codebases, CMS plugins and projects with no framework — anywhere a single dependency that reliably sends email is exactly the right size of tool. It is also the reason so much of the PHP web works: WordPress alone means it is running on a substantial share of the internet.
Contributing
Bug reports, security advisories and pull requests belong on the project’s repository at github.com/PHPMailer/PHPMailer. If you find something inaccurate on these documentation pages, that is ours — let us know.
