Send email with PHP over SMTP (PHPMailer)

Working PHP SMTP example with PHPMailer: STARTTLS on port 587, authentication, HTML + plain text, attachments, and why mail() lands in spam. Includes the Composer install, error handling and the Gmail/SES/Office 365 host table.

Install

shell
composer require phpmailer/phpmailer

Complete example

PHP — STARTTLS on 587, credentials from the environment
<?php
require 'vendor/autoload.php';

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

$mail = new PHPMailer(true);
try {
    $mail->isSMTP();
    $mail->Host       = getenv('SMTP_HOST') ?: 'smtp.queensmtp.com';
    $mail->Port       = 587;
    $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;   // use ENCRYPTION_SMTPS + port 465 for implicit TLS
    $mail->SMTPAuth   = true;
    $mail->Username   = getenv('SMTP_USER');
    $mail->Password   = getenv('SMTP_PASS');

    $mail->setFrom('hello@yourdomain.com', 'Your App');   // must be a verified sending domain
    $mail->addAddress('customer@example.com', 'Customer');
    $mail->addReplyTo('support@yourdomain.com');

    $mail->isHTML(true);
    $mail->Subject = 'Your receipt';
    $mail->Body    = '<h1>Thanks for your order</h1><p>Order #1042 is confirmed.</p>';
    $mail->AltBody = "Thanks for your order\nOrder #1042 is confirmed.";
    // $mail->addAttachment('/path/invoice-1042.pdf');

    $mail->send();
    echo "sent\n";
} catch (Exception $e) {
    error_log('SMTP error: ' . $mail->ErrorInfo);
}

What to know

  • PHP's built-in mail() hands the message to the local sendmail/Exim with no authentication, from the web server's IP — that is why contact-form mail from shared hosting goes to spam or nowhere. SMTP with authentication fixes it.
  • Set SMTPDebug = 2 while testing to see the full conversation; turn it off in production.
  • Keep one PHPMailer instance and call addAddress/send in a loop for batches — a new TCP+TLS connection per message is slow and trips rate limits.
  • On Laravel/Symfony use the framework mailer (see the Laravel page); PHPMailer is for plain PHP and WordPress plugins.

Same code, different provider

Only the host, port and credentials change. The rules per provider are on the SMTP settings hub.

ProviderHostPortUsernamePasswordNote
QueenSMTP smtp.queensmtp.com587 (STARTTLS) / 465 (SSL) / 2525SMTP username from the dashboardSMTP password$5/year + $0.10 per 1,000; 100 free/day
Gmail smtp.gmail.com587 / 465you@gmail.comApp Password500/day, From must be the account
Amazon SES email-smtp.<region>.amazonaws.com587 / 465 / 2587SES SMTP usernameSES SMTP passwordsandbox 200/day until approved
Office 365 smtp.office365.com587user@yourdomain.commailbox passwordSMTP AUTH must be enabled; 30/min
SendGrid smtp.sendgrid.net587 / 465 / 2525apikeyAPI keyno free plan since 2025

Credentials for the example above

Create a free account, verify your sending domain (SPF + DKIM shown in the dashboard), add an SMTP credential, and paste it into the code. 100 emails a day free; $5/year plus $0.10 per 1,000 after that. Prefer HTTPS? The same account has a REST email API.

Get SMTP credentials Test your settings first

Other languages and frameworks

Examples tested 2026-08-30. If a library changed its API, tell us.

PHP SMTP — questions

Most shared hosts route mail() through an unauthenticated local mailer that receivers distrust, and many block port 25 entirely. Use SMTP with authentication via PHPMailer or the framework mailer.

587 with ENCRYPTION_STARTTLS is the standard submission setup; 465 with ENCRYPTION_SMTPS works where 587 is blocked.