PHPMailer’s default transport is PHP’s built-in mail() function. It needs no configuration at all, which makes it the fastest way to get a message out — and the reason it keeps being used in places where it should not be. This page covers how to use it properly, and how to recognise when you have outgrown it.
Examples target PHPMailer 6.x. For the transport you probably want instead, see the SMTP example.
Minimal example
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'vendor/autoload.php';
$mail = new PHPMailer(true);
try {
$mail->setFrom('from@example.com', 'Your App');
$mail->addAddress('recipient@example.com', 'Recipient Name');
$mail->Subject = 'Contact form submission';
$mail->Body = "Name: Jane Doe\nMessage: Hello there.";
$mail->send();
echo 'Sent.';
} catch (Exception $e) {
echo 'Failed: ' . $mail->ErrorInfo;
}
No host, no port, no credentials. mail() is the default so there is nothing to select, though you can be explicit with $mail->isMail(); if you have switched transports earlier in the script.
A realistic example
HTML with a plain-text alternative, UTF-8, an attachment and a proper envelope sender:
$mail = new PHPMailer(true);
try {
$mail->CharSet = PHPMailer::CHARSET_UTF8;
$mail->setFrom('noreply@example.com', 'Your App');
$mail->addAddress('recipient@example.com');
$mail->addReplyTo('support@example.com', 'Support');
$mail->Sender = 'bounces@example.com'; // Return-Path
$mail->addAttachment('/path/to/invoice.pdf', 'invoice.pdf');
$mail->isHTML(true);
$mail->Subject = 'Your invoice';
$mail->Body = '<p>Hello,</p><p>Your invoice is <b>attached</b>.</p>';
$mail->AltBody = "Hello,\n\nYour invoice is attached.";
$mail->send();
} catch (Exception $e) {
error_log('Send failed: ' . $mail->ErrorInfo);
}
Set CharSet to UTF-8 whenever accents or non-Latin characters can appear — the ISO-8859-1 default will corrupt them. $Sender sets the envelope sender, which is where bounces are delivered and which PHPMailer passes to sendmail as -f. Without it, bounces go wherever the server decides, which usually means nowhere you will ever look.
The limitation that matters
mail() returns success as soon as the local mail transfer agent accepts the message for queuing. That is the entire guarantee. If the MTA then fails to relay it, if the recipient’s server rejects it, or if there is no MTA configured and the message is silently discarded, mail() has already told you everything went fine.
With SMTP you get the server’s actual response — an authentication failure, a rejected recipient, a rate limit, a content block — and you can log it or retry. With mail() there is nothing to log, because from PHP’s point of view nothing went wrong.
This is why transactional email should not use mail(). A password reset that silently fails is worse than one that errors loudly.
Deliverability
mail() sends from your web server’s IP address. That address is very likely to be shared, unlikely to have matching reverse DNS, and quite possibly on a blocklist already because of a neighbour. Meanwhile the From domain in your message almost certainly does not authorise that server in its SPF record, and nothing is signing the message with DKIM.
The result is predictable: Gmail and Outlook filter it, or refuse it outright. Hosted SMTP providers exist to solve exactly this — dedicated reputation, aligned SPF, and DKIM signing on every message.
When mail() is a reasonable choice
- A properly configured MTA on the same host. If Postfix is set up with valid reverse DNS, SPF alignment and DKIM signing,
mail()is thin plumbing over a sound setup. - Internal or same-domain mail. An alert to an address on the same server never leaves the machine and never faces a spam filter.
- Low-stakes notifications where a lost message costs nothing.
- Local development pointed at a catcher like MailHog or Mailpit.
When to move to SMTP
- The message matters: password resets, receipts, order confirmations, anything the user is waiting for.
- You need to know whether it was delivered.
- Recipients are on Gmail, Outlook, Yahoo or any large provider.
- Messages go missing and you have no way to find out why.
- You are on shared hosting and have no control over the sending IP.
Switching is five lines — see the SMTP example. There is no reason to delay it.
Errors specific to this transport
Could not instantiate mail function
mail() itself failed, which almost always means there is no working MTA behind it. Confirm from the command line:
php -r 'var_dump(mail("you@example.com", "test", "body"));'
sendmail -bv you@example.com
If mail() returns false on its own, the problem is server configuration, not PHPMailer. On most shared hosting the answer is to use SMTP instead — many providers disable mail() deliberately.
Mail sent successfully but never arrives
The expected failure mode. mail() reported that the MTA queued it; what happened next is invisible to PHP. Check the MTA log, which is where the real story is:
tail -f /var/log/mail.log # Debian/Ubuntu
tail -f /var/log/maillog # RHEL/CentOS
The From address gets rewritten
Some MTAs force the envelope sender to a local account. Setting $Sender asks PHPMailer to pass -f, which sometimes helps — but only if the MTA permits that user to set it. If UseSendmailOptions is false, the flag is never passed at all.
Messages arrive with broken accents
$CharSet is still at its ISO-8859-1 default. Set PHPMailer::CHARSET_UTF8.
Testing locally
Never test deliverability against real inboxes — you will train providers to distrust your domain before you have a working setup. Run a local catcher and point PHP at it:
# Mailpit, for example
$mail->isSMTP();
$mail->Host = '127.0.0.1';
$mail->Port = 1025;
$mail->SMTPAuth = false;
$mail->SMTPAutoTLS = false;
Every message is captured and viewable in a web interface, including the raw MIME source. Note that this uses the SMTP transport — which is itself a reason to build on SMTP from the start, since the same code then works in development and production with only the host changing.
See also
- SMTP Example — the recommended transport, in full.
- Examples — every transport including sendmail and Gmail.
- Tutorial — the guided introduction.
- Properties Reference —
$Sender,$CharSet,$Sendmailand the rest.
