How I broke it, what that cryptic error actually means, and the cleaner code I use today plus a few drills you can practice on your own.
Error Code
I just wanted to ship a friendly “Hello” from a PHP page. Instead, Gmail greeted me with this brick wall:
Authentication failure [SMTP: SMTP server does no support authentication
(code: 250, response: mx.google.com at your service, [98.117.99.235]
SIZE 35651584 8BITMIME STARTTLS ENHANCEDSTATUSCODES PIPELINING)]
At first glance it screams, “Gmail won’t let you log in,” but hidden in the noise is the clue: STARTTLS.
My First Snippet
<?php
require_once "Mail.php"; // PEAR Mail
$from = "Sandra Sender <sender@example.com>";
$to = "Ramona Recipient <ramona@microsoft.com>";
$subject = "Hi!";
$body = "Hi,\n\nHow are you?";
$host = "smtp.gmail.com";
$port = 587; // Gmail’s TLS port
$username = "testtest@gmail.com";
$password = "testtest"; // never hard-code real secrets
$headers = [
'From' => $from,
'To' => $to,
'Subject' => $subject
];
$smtp = Mail::factory('smtp', [
'host' => $host,
'port' => $port,
'auth' => true,
'username' => $username,
'password' => $password
]);
$result = $smtp->send($to, $headers, $body);
echo PEAR::isError($result)
? "<p>" . $result->getMessage() . "</p>"
: "<p>Message successfully sent!</p>";
?>
Why it Fizzled
Symptom | Plain-English cause |
---|---|
SMTP server does no support authentication (code: 250) | Gmail is happy to talk, but on port 587 it demands a STARTTLS handshake before it will listen to any username or password. PEAR Mail tried to log in first—Gmail politely refused. |
A couple more rules bit me too:
- Since May 2022 Gmail blocks “Less Secure Apps.” You need an app-specific password or OAuth2 your everyday login password no longer works.
- PEAR Mail doesn’t flip the TLS switch automatically.
Quick Patch While Staying on PEAR Mail
$smtp = Mail::factory('smtp', [
'host' => 'tls://smtp.gmail.com', // tell PEAR to negotiate TLS
'port' => 587,
'auth' => true,
'username' => getenv('GMAIL_USER'), // read from env vars
'password' => getenv('GMAIL_APP_PASS')
]);
This does work, but PEAR Mail is frozen in the early 2010s and has no native OAuth2 helper. That sent me hunting for a friendlier library.
The Clean UpGrade: PHPMailer
Install in one line:
composer require phpmailer/phpmailer
Drop the sample below into send-gmail.php
and run it:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'vendor/autoload.php';
$mail = new PHPMailer(true); // throws Exceptions
try {
/* --- server --- */
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com';
$mail->SMTPAuth = true;
$mail->Username = getenv('GMAIL_USER'); // me@gmail.com
$mail->Password = getenv('GMAIL_APP_PASS'); // 16-char app password
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
/* --- recipients --- */
$mail->setFrom('sender@example.com', 'Sandra Sender');
$mail->addAddress('ramona@microsoft.com', 'Ramona Recipient');
$mail->addReplyTo('noreply@example.com', 'No-reply');
/* --- content --- */
$mail->isHTML(true);
$mail->Subject = 'Hi!';
$mail->Body = '<p>Hi Ramona,<br><br>How are you?</p>';
$mail->AltBody = "Hi Ramona,\n\nHow are you?";
$mail->send();
echo 'Message sent!';
} catch (Exception $e) {
echo "Mailer Error: {$mail->ErrorInfo}";
}
?>
Why this works
SMTPSecure = STARTTLS
forces encryption before AUTH.- App passwords dodge Gmail’s “Less Secure Apps” block without a full OAuth2 dance.
- PHPMailer prints crystal-clear errors when something goes sideways.
Stretch Goals
Drill | One-liner to drop in |
---|---|
Attach a file | $mail->addAttachment(__DIR__.'/invoice.pdf'); |
Send CC/BCC | $mail->addCC('boss@example.com'); $mail->addBCC('audit@example.com'); |
Watch every SMTP command | $mail->SMTPDebug = 2; (great on a staging box—turn off in production) |
Load secrets from .env | composer require vlucas/phpdotenv then $_ENV['GMAIL_USER'] |
HTML contact form | Pair a small index.html with action="send-gmail.php" ; sanitize POST fields before calling $mail->Body . |
Full OAuth2 (hard-mode) | Register a Google Cloud project, enable Gmail API, and use PHPMailer’s OAuth class. |
Work through them one at a time enable SMTPDebug
, re-run, and watch the live conversation: EHLO
, STARTTLS
, AUTH
, MAIL FROM
, RCPT TO
, DATA
, QUIT
. Seeing the handshake click into place is oddly satisfying.
Final Thought
The scary-looking error wasn’t cryptic at all once I read between the brackets it was Gmail saying, “Talk TLS to me first.” As soon as I negotiated encryption and switched to an app password, Gmail behaved like any normal SMTP server.