Last updated: August 29, 2026.
A successful send call means a server accepted the message; it does not prove inbox delivery. Find the last system that accepted it, then inspect that system’s response or log.
Enable temporary SMTP diagnostics
<?php
use PHPMailer\PHPMailer\SMTP;
$mail->SMTPDebug = SMTP::DEBUG_SERVER;
$mail->Debugoutput = static function (string $line, int $level): void {
error_log('SMTP ' . $level . ': ' . $line);
};Work through the delivery chain
- Confirm sender, recipient, host, port, and TLS mode.
- Inspect provider logs for rejection, deferral, or delivery.
- Verify SPF, DKIM, and DMARC alignment.
- Test a simple message without attachments.
- Check suppression lists, spam placement, and later bounces.
Locate the last successful handoff
Separate application construction, SMTP acceptance, provider processing, recipient rejection, and spam placement. The last system that accepted the message determines which log or event should be checked next.
Use a simple message to a controlled address, capture the SMTP response, and compare provider delivery events with later bounces. Turn verbose protocol logging off after the test.
- Verify sender-domain authentication.
- Check suppression lists.
- Do not retry permanent recipient failures.
Keep resource limits explicit and make failure cleanup part of the example. Log enough context to diagnose the operation without recording passwords, private message bodies, or uploaded file contents.
Continue with SMTP with PHPMailer, queued retries, and HTTP email APIs.
Practical implementation check
Put the operation in a small function or service with explicit inputs and a clear failure result. Test invalid input, missing extensions, permission failures, and concurrent requests. Production logs should identify the operation and record ID without storing secrets or private content. Clean up partially created files or queue records when any later step fails.
Return a clear result to the caller and keep user-facing messages separate from detailed diagnostics. This makes the same code usable from a web request, command-line job, or queue worker.
Reference: PHPMailer troubleshooting guide.
For a PHPRunner-oriented configuration walkthrough, see this SMTP email guide.