The classic email sending library for PHP
GNU Lesser General Public License v2.1

I am running PHP 5.6 and Steven Maguire’s Microsoft Provider extension to thephpleague’s oauth2-client in order to use the MSFT Identity Platform V2.0 authorization and token endpoints and the MSFT Graph V1.0 API with PHPMailer.
The V2 end-points and Graph are needed to support SMTP AUTH with Oauth2 (as announced in May 2020), and my MSFT tenant has SMTP AUTH enabled (MSFT is disabling it by default for new tenants).

I have made the obvious changes (below) needed to Steven’s code to support v2.0 endpoints and the Graph API v1.0.

Running my get_oauth_token manually successfully gives a refresh token that is pasted into my PHPMailer invocation code.
But subsequently invoking PHPMailer results in a 535 5.7.3 authentication failure:
(A 535 5.7.3 fail code is not in RFC 4954’s code list but seems a common enough ‘invalid credentials’ error.)
I have posted variants of this problem on thephpleague/oauth2-client and stevenmaguire/oauth2-client repositories and also on the thenetworg/oauth2-azure (which has its own provider code)

SMTP ERROR: AUTH command failed: 535 5.7.3 Authentication unsuccessful []

However: running get_oauth_token manually is recorded in AAD Sign-ins but subsequently invoking PHPMailer is not. It appears that whatever calls get_oauth_token (whether directly or by callback from an endpoint) is not doing so and hence not obtaining authentication.

I am baffled and clearly doing something daft. Any suggestions are most welcome, especially from anyone who has successfully implemented this using v2.0 endpoints and the Graph API.


My compose.json ‘requires’ just:

 "phpmailer/phpmailer": "dev-master"     …… which should pick up PHPMailer 6.1
 "stevenmaguire/oauth2-microsoft": "dev-master"

My changes to the Steven Maguire’s Provider code are in vendor/stevenmaguire/oauth2-microsoft/src/Provider/Microsoft.php:

    public $defaultScopes = ['Mail.Send SMTP.Send offline_access openid profile email User.Read']
    protected $urlAuthorize = ''
    protected $urlAccessToken = ''
    protected $urlResourceOwnerDetails = ''

My PHPMailer invocation is:

require 'vendor/autoload.php';

use phpmailer\phpmailer\OAuth;
use phpmailer\phpmailer\PHPMailer;
use phpmailer\phpmailer\SMTP;
use phpmailer\phpmailer\Exception;
use Stevenmaguire\OAuth2\Client\Provider\Microsoft; 

$mail = new PHPMailer;
$mail->Host = '';
$mail->SMTPAuth = true;                  // Enable SMTP authentication
$mail->AuthType = 'XOAUTH2';             // Select OAUTH2 
$mail->SMTPSecure = 'tls';               // Enable TLS encryption
$mail->Port = 587;
//$mail->Username = [my email address];    //only for Basic Authentication AuthType
//$mail->Password = [my email password];  //only for Basic Authentication AuthType

$username = ‘[my email address]’;    
$clientId = '[my client ID from AAD]';
$clientSecret = '[my client secret from AAD]';
$redirectURI = '[URI of my get_oauth_token.php module, as registered with AAD';
$refreshToken = '[1049 character token, extracted from get_oauth_token’s response] ';
$mail -> refresh_token = $refreshToken;

$provider = new Microsoft([
    'clientId'          => $clientId,
    'clientSecret'      => $clientSecret,
    'redirectUri'       => $redirectURI

$provider->urlAPI = ‘’;
$provider->scope = ‘openid  SMTP.Send Mail.Send offline_access profile email User.Read’;

        new OAuth(
                'provider' => $provider,
                'clientId' => $clientId,
                'clientSecret' => $clientSecret,
               ‘refreshToken' => $refreshToken, 
                'userName' =>$username
// The remaining code is purely PHPMailer and works fine with Basic Authentication

… just a Microsoft-specific and slightly pruned version of Steven’s own. The ‘Select Provider’ is thus strictly unnecessary.

namespace PHPMailer\PHPMailer;
use Stevenmaguire\OAuth2\Client\Provider\Microsoft;

if (!isset($_GET['code']) && !isset($_GET['provider'])) {
<body>Select Provider:<br/>
<a href='?provider=Microsoft'>Microsoft/Outlook/Hotmail/Live/Office365</a><br/>

require 'vendor/autoload.php';


$providerName = '';

if (array_key_exists('provider', $_GET)) {
    $providerName = $_GET['provider'];
    $_SESSION['provider'] = $providerName;
} elseif (array_key_exists('provider', $_SESSION)) {
    $providerName = $_SESSION['provider'];

$clientId = [my client ID from AAD];
$clientSecret = [my client secret from AAD]';
$redirectUri = [URI of this module, as registered with AAD ';

$params = [
    'clientId' => $clientId,
    'clientSecret' => $clientSecret,
    'redirectUri' => $redirectUri,
    'accessType' => 'offline'

$options = [];
$provider = null;

switch ($providerName) {
        case 'Microsoft':
        $provider = new Microsoft($params);

$provider->scope = ‘openid  SMTP.Send Mail.Send offline_access profile email User.Read’;

if (!isset($_GET['code'])) {
    // If we don't have an authorization code then get one
    $authUrl = $provider->getAuthorizationUrl($options);
    $_SESSION['oauth2state'] = $provider->getState();
    header('Location: ' . $authUrl);

// Check given state against previously stored one to mitigate CSRF attack
} elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) {
    exit('Invalid state');
} else {
    // Try to get an access token (using the authorization code grant)
    $token = $provider->getAccessToken(
            'code' => $_GET['code']
    echo 'Refresh Token: ', $token->getRefreshToken();

Problem description

I used a (previously errorneous) script that should attach all files within a directory to a PHPMailer object using addAttachment. The script did unfortunately not exclude paths to (sub) directories. Here is what happens:

If you use the addAttachment function and pass a directory path rather than a file path, then

  • the file_get_contents function produces a Notice file_get_contents(): Read of 8192 bytes failed with errno=21 Is a directory in ...
  • A zero byte attachment with the directory's name as file name is added to the email.

Code to reproduce



The function fileIsAccessible uses file_exists to check whether a file path is valid. But this function returns true if the given path is a file or a directory ( The problem could be solved by using is_file instead.

Since some months i user Phpmailer to send all my company emails to replace Outlook.
All is ok ... apparently, but we never get anti-spam/mailinblack verification email.
btw if we send the same mail with Outlook, we get back mailinblack verification email.

Emails are send via a domain with valid SPF/DKIM.
I verify my configuration and all seems ok.

That's a part of my header code to see if you've got an idea:

$mail = new PHPMailer\PHPMailer\PHPMailer;
$mail->Host = $compte["HOST"];
$mail->Port = $compte["PORT_OUT"];
$mail->SMTPAuth = true;
$mail->XMailer = " ";
$mail->Username = $compte["LOGIN"];
$mail->Password = $compte["PASS"];
if(isset($compte["DKIM_DOMAIN"])) {
$mail->DKIM_domain = $compte["DKIM_DOMAIN"];
$mail->DKIM_private = $compte["DKIM_PRIVATE"];
$mail->DKIM_selector = $compte["DKIM_SELECTOR"];
$mail->DKIM_passphrase = $compte["DKIM_PASSPHRASE"];
$mail->DKIM_identity = $compte["DKIM_IDENTITY"];
$mail->CharSet = "UTF-8";
$mail->Encoding = "base64";

$mail->addReplyTo($deMail, $de);
$mail->Sender = $deMail;
$mail->addCustomHeader("Sender",$de . " <" . $deMail . ">");
$mail->addCustomHeader("List-Unsubscribe","<mailto:" . $deMail . ">");

There's something more to do ?

|| !preg_match('/^([a-zA-Z\d.-]*|\[[a-fA-F\d:]+\])$/', $host)
is simply a bad practice. You're trying to validate hostnames with a regex that doesn't conform to all past or future RFCs. It's good to validate against the empty string, and to classify ip4, ipv6, etc, but the regex here doesn't conform to valid hostnames used outside of DNS. And there are plenty of reasons to use hostnames that are not used with DNS, for instance, docker hosts, internal or unroutable hosts, etc.

Can you add feature Gmail XOAUTH2 with access token?
Access token expires in short time, it is safe, while refresh token (OAuth2 provider) is not so, it is not suitable for some environments.

composer : The term 'composer' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling
of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1

  • composer require phpmailer/phpmailer
  •   + CategoryInfo          : ObjectNotFound: (composer:String) [], CommandNotFoundException
      + FullyQualifiedErrorId : CommandNotFoundException

I followed instruction here , I have a successful result asking openssl to test the connection using CA cert. But I'm still receiving the issue.

2020-05-29 13:19:55 Connection: opening to, timeout=300, options=array()
2020-05-29 13:19:55 Connection: opened
2020-05-29 13:19:55 SMTP INBOUND: " ESMTP Exim 4.93 #2 Fri, 29 May 2020 15:19:55 +0200"
2020-05-29 13:19:55 SMTP INBOUND: "220-We do not authorize the use of this system to transport unsolicited,"
2020-05-29 13:19:55 SMTP INBOUND: "220 and/or bulk e-mail."
2020-05-29 13:19:55 SERVER -> CLIENT: ESMTP Exim 4.93 #2 Fri, 29 May 2020 15:19:55 +0200 220-We do not authorize the use of this system to transport unsolicited, 220 and/or bulk e-mail.
2020-05-29 13:19:55 CLIENT -> SERVER: EHLO
2020-05-29 13:19:55 SMTP INBOUND: " Hello []"
2020-05-29 13:19:55 SMTP INBOUND: "250-SIZE 52428800"
2020-05-29 13:19:55 SMTP INBOUND: "250-8BITMIME"
2020-05-29 13:19:55 SMTP INBOUND: "250-PIPELINING"
2020-05-29 13:19:55 SMTP INBOUND: "250-AUTH PLAIN LOGIN"
2020-05-29 13:19:55 SMTP INBOUND: "250-STARTTLS"
2020-05-29 13:19:55 SMTP INBOUND: "250 HELP"
2020-05-29 13:19:55 SERVER -> CLIENT: Hello []250-SIZE 52428800250-8BITMIME250-PIPELINING250-AUTH PLAIN LOGIN250-STARTTLS250 HELP
2020-05-29 13:19:55 CLIENT -> SERVER: STARTTLS
2020-05-29 13:19:55 SMTP INBOUND: "220 TLS go ahead"
2020-05-29 13:19:55 SERVER -> CLIENT: 220 TLS go ahead
2020-05-29 13:19:55 Connection failed. Error #2: stream_socket_enable_crypto(): SSL operation failed with code 1. OpenSSL Error messages:error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed [/home/xxx/vendor/phpmailer/phpmailer/src/SMTP.php line 426]
SMTP Error: Could not connect to SMTP host.
2020-05-29 13:19:55 CLIENT -> SERVER: QUIT
2020-05-29 13:19:55
2020-05-29 13:19:55
2020-05-29 13:19:55
2020-05-29 13:19:55 SMTP INBOUND: ""
2020-05-29 13:19:55
2020-05-29 13:19:55
2020-05-29 13:19:55 Connection: closed
SMTP Error: Could not connect to SMTP host.

If I use
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true

I receive
020-05-29 13:32:04 Auth method requested: UNSPECIFIED
2020-05-29 13:32:04 Auth methods available on the server: PLAIN,LOGIN
2020-05-29 13:32:04 Requested auth method not available:
2020-05-29 13:32:04 Auth method selected: LOGIN
2020-05-29 13:32:04 CLIENT -> SERVER: AUTH LOGIN
2020-05-29 13:32:06 SMTP INBOUND: "535 Incorrect authentication data"
2020-05-29 13:32:06 SERVER -> CLIENT: 535 Incorrect authentication data
2020-05-29 13:32:06 SMTP ERROR: Password command failed: 535 Incorrect authentication data
SMTP Error: Could not authenticate.

But I'm sure that user and password are correct!

How to sort this out?
I'm on PHP 7.3 on Centos 7

Thank you,

Please check these things before submitting your issue:

  • Read the error message you're seeing - it often tells you what is wrong, and may contain useful links & instructions
  • Make sure you're using the latest version of PHPMailer
  • Check that your problem is not dealt with in the troubleshooting guide, especially if you're having problems connecting to Gmail or GoDaddy
  • Include sufficient code to reproduce your problem
  • If you're having an SMTP issue, include the debug output generated with SMTPDebug = 2 set
  • If you have a question about how to use PHPMailer (rather than reporting a bug in it), tag a question on Stack Overflow with phpmailer, but search first!

Problem description

There is extra space/s added to longer subject after upgrading from 5.2 to 6.6.

Code to reproduce

$mail = new PHPMailer;
$mail->CharSet = 'UTF-8';
$mail->Subject = 'This is actually very long subject that will result in extra space';
$mail->Body = 'Content';

Is there a way how to prevent this behavior in 6.6?



Deprecated : explode(): Passing null to parameter #2 ($string) of type string is deprecated in
...\PHPMailer\PHPMailer\PHPMailer.php on line 2104

Thanks for checking $this->Host before ;)


I have set up PHPMailer and can send emails successfully. When I use addBCC( $address ) or addCustomHeader( 'BCC', $address ) however, nothing gets sent to the BCC address. The message still reaches the To address. I am using a linux server, with PHP 5.6.

Code to reproduce

Using SMTP and HTML:

$mail->addBCC( '' );


$mail->addCustomHeader( 'BCC', '' );

Debug output

No errors or debug output.


Today I upgraded from version 6.0.1 to 6.6.3 of PHPMailer. This corrected some previous issues, but there is a new one.
The case:

  • Using PHP mail() by applying isMail()
  • Applying DKIM-signature
  • Multiple BCC-addressees
  • No To or CC-addressees

This results an invalid DKIM-signature.

What happens is the following:

  1. In preSend() line 1607 the To-addressee undisclosed-recipients:; is added to the headers in $this->mailHeader
  2. This is used to construct the DKIM-signature (in particular for the z-field)
  3. Next in mailSend() the variable $to will be empty since there are no To-addressees and the email will be sent as such

This results in the following example headers (with invalid DKIM-signature):

Received: by mailserver (Postfix, from userid 33) id C53F3520B8D; Sat, 20 Aug 2022 14:00:55 +0000 (UTC)
Subject: Testmail
DKIM-Signature: v=1; [...] h=Date:From:Reply-To:Message-ID:X-Mailer:MIME-Version:Content-Type:To:Subject; z=Date:Sat,=2020=20Aug=202022=2016:00:55=20+0200 |[...]To:undisclosed-recipients:=3B |Subject:Testmail;[...]

I was able to correct the behaviour by adding the following line after line 1894 in mailSend():
if($to == "") $to = "undisclosed-recipients:;";

This results in the following example headers (with valid DKIM-signature):

Received: by mailserver (Postfix, from userid 33) id CE9AE520036; Sun, 21 Aug 2022 08:45:47 +0000 (UTC)
To: undisclosed-recipients:;
Subject: Testmail
DKIM-Signature: v=1; [...] h=Date:From:Reply-To:Message-ID:X-Mailer:MIME-Version:Content-Type:To:Subject; z=Date:Sun,=2021=20Aug=202022=2010:45:47=20+0200 |[...]|To:undisclosed-recipients:=3B |Subject:Testmail;[...]


I found no success in debugging my application in multiple ways, so I decided to share my problem here.
I am using a mailing list defined in my system and it is being retrieved while user decides to send a group email.
In some cases a random point is being added to the recipient address and the email is not being delivered.
Example: is being changed to
It also appears to be changed in the email prefix, not only in domain.

I have upgraded the version to the latest (6.6.3 right now) and still nothing changed.

I've double-checked all the addresses in DB and all were correct. I've also printed out all the emails just before the send() function call just to see if there is any way that it could be modified just before and all the addresses were correct.

There might be some issues with email filtering system that my company uses, but I want to see if there is any possibility that it is an issue on the library's side.

Thanks for the help!


Why not use '$this-secureHeader($this-Subject)' everywhere

The scenario is that when the email header and content are written by external clients, how to ensure that the calls in each place are safe.
Including 'bcc', 'cc', etc. are all content that may be entered by the client, and they are not safely filtered from the beginning, is there a place for repeated security filtering?


Lines 1927 to 1931 in bf99c20



Lines 1758 to 1762 in bf99c20


2022-08-08 22:25:15 SMTP ERROR: Failed to connect to server: php_network_getaddresses: getaddrinfo failed: Host desconocido. (0) 2022-08-08 22:25:15 SMTP connect() failed.

function enviarEmail($email, $nombre, $asunto, $cuerpo){

	require_once '../PHPMailer/PHPMailerAutoload.php';
	$mail = new PHPMailer();

	$mail->SMTPDebug = 2;
	$mail->Host = ''; //Modificar
	$mail->SMTPAuth = true;
	$mail->Username = ''; //Modificar
	$mail->Password = 'clave'; //Modificar
	$mail->SMTPSecure = 'tls'; //Modificar
	$mail->Port = 465; //Modificar
	$mail->setFrom('','Juan Perez'); //Modificar
	$mail->addAddress($email, $nombre);
	$mail->Subject = $asunto;
	$mail->Body    = $cuerpo;
	return true;
	return false;

help me!

My client uses Apple Mail on his iPhone to respond to incoming website emails where we have a PHPMailer contact form. About half the time when he replies, the "replyto" email address is ignored by Mail and the "from" address is used instead. Since we're using the postmaster address in the "from" to improve email deliverability, he ends up replying to Postmaster instead of the customer. I use Gmail, and I've never experienced this problem with my own forms using the same type of code so this seems like an issue with Apple Mail. Nevertheless, I thought I'd ask if there was something I could do to solve this. Maybe there's something wrong with my code?


if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['contact'])) {

  // validation code where $email and $name are defined as submitted and $error is true if validation fails

  if ($error === false) {

    $email_package = new Email();

    // assemble HTML message to be emailed
    $email_html = "<html email>";

    $email_package->addReplyTo($email, $name);
    $email_package->addRecipient('', 'Customer Service');
    $email_package->setSubject('Message from Customer');

    if($email_package->mailSuccess === false) {
      error_log("FAIL: " . $email_package->mailError);

email script:


use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;

require 'PHPMailer/Exception.php';
require 'PHPMailer/PHPMailer.php';
require 'PHPMailer/SMTP.php';

class Email {
  private $mailer;

  var $mailSuccess = false;
  var $mailError = '';

  function __construct() {
    $this->mailer = new PHPMailer(true);
    $this->mailer->Host = '';
    $this->mailer->SMTPAuth = true;
    $this->mailer->Username = '';
    $this->mailer->Password = 'password';
    $this->mailer->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
    $this->mailer->Port = 465;
    $this->mailer->CharSet = PHPMailer::CHARSET_UTF8;
    $this->mailer->DKIM_domain = $domain;
    $this->mailer->DKIM_selector = 'selector';
    $this->mailer->DKIM_identity = '';
    $this->mailer->DKIM_private = 'private.key';
    $this->mailer->setFrom('', 'Postmaster');

  public function addReplyTo($address, $name = '') {
    return $this->mailer->addReplyTo($address, $name);

  public function addRecipient($address, $name = '') {
    return $this->mailer->addAddress($address, $name);

  public function setSubject($subject) {
    $this->mailer->Subject = $subject;

  public function isHTML($bool) {

  public function setBodyHTML($email_html) {
    $this->mailer->Body = $email_html;

  public function setBodyText($email_text) {
    $this->mailer->AltBody = $email_text;

  public function send() {
    try {
      $this->mailSuccess = true;
    } catch (Exception $e) {
      $this->mailError = $e->errorMessage();
      error_log('PHPMailer Error: ' . $this->mailError);
    } catch (\Exception $e) {
      $this->mailError = $e->getMessage();
      error_log('PHPMailer Error: ' . $this->mailError);

Problem description

When sending DKIM-signed mails through a specific SMTP server from my OVH hoster with my credentials, the received body hash is wrong, while it's OK with 2 other SMTP servers. I checked it via Gmail and Any idea what causes this?

Code to reproduce

Too risky to give you this possibility here. Send me a personal message and I'll provide you with the URL to reproduce this issue.

Debug output

Gmail : "body hash did not verify"

Dear @PHPMailer team,

In first, I wish you a Happy New Year!

Can you add supports of :

  • SCRAM-SHA-256
  • SCRAM-SHA-512
  • SCRAM-SHA3-512

You can add too:

  • SCRAM-SHA-224
  • SCRAM-SHA-384

"When using the SASL SCRAM mechanism, the SCRAM-SHA-256-PLUS variant SHOULD be preferred over the SCRAM-SHA-256 variant, and SHA-256 variants [RFC7677] SHOULD be preferred over SHA-1 variants [RFC5802]".

-PLUS variants:



  • RFC5803: Lightweight Directory Access Protocol (LDAP) Schema for Storing Salted: Challenge Response Authentication Mechanism (SCRAM) Secrets:




Linked to:

Hello author, I am using PHPMailer to send emails on my site and it's working perfectly but emails go to spam folder when hyperlinks are included in the mail body.

I could really use a help from you on this. Thanks.

I am using Cacti as monitoring server and it was working fine unless i changed server IP. After change of ip, it stop sending emails. It is giving error

cacti error