guides
Send Email in PHP with SMTP (PHPMailer, HTML, Attachments, and Testing)
Learn how to send email in PHP with SMTP and PHPMailer, including TLS auth, HTML bodies, attachments, form sends, MailSlurp API checks, and inbox testing.
If you searched for "send email php", "php send mail", or "send mail using SMTP in PHP", the safest production path is:
- use PHPMailer or an email API instead of raw
mail()for important workflows - load SMTP credentials from environment variables
- enforce TLS and sender-domain authentication
- test inbox receipt, links, HTML, and attachments before release
PHP can send email quickly, but quick examples often skip the parts that fail in production: wrong ports, stale SMTP passwords, missing SPF/DKIM alignment, blocked outbound traffic, and form inputs that create unsafe messages. This guide keeps the setup practical and testable.
Choose the right PHP email path
| Need | Recommended path | Why |
|---|---|---|
Maintain an old site that uses mail() |
Move critical sends to PHPMailer SMTP | You get explicit auth, TLS, errors, and portability. |
| Send signup, reset, billing, or support emails | PHPMailer with environment-backed SMTP settings | Works well in common PHP hosting and framework jobs. |
| Send from a serverless or API-first workflow | MailSlurp API or SDK | Easier to test, log, and assert from CI. |
| Validate delivery before release | MailSlurp inbox plus wait-for-email checks | Confirms the message actually arrives with the expected content. |
If you still need to understand the native function, use the PHP mail function guide. For modern application delivery, SMTP-configured PHPMailer or an API-based workflow is usually easier to operate.
Install PHPMailer
Install PHPMailer with Composer:
composer require phpmailer/phpmailer
Commit composer.json and composer.lock, but keep SMTP credentials out of the repository.
Configure SMTP settings with environment variables
Set these values in your hosting provider, .env loader, deployment platform, or CI secret store:
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=smtp-user
SMTP_PASSWORD=smtp-password
SMTP_FROM_ADDRESS=no-reply@example.com
SMTP_FROM_NAME="Example App"
Use port 587 with STARTTLS for most modern SMTP submission flows unless your provider documents a different setting. Review SMTP ports and STARTTLS vs SSL/TLS when validating the configuration.
Send email in PHP with PHPMailer and SMTP
This is the basic production-shaped PHPMailer send:
<?php
require __DIR__ . "/vendor/autoload.php";
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer;
$mail = new PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = getenv("SMTP_HOST");
$mail->Port = (int) getenv("SMTP_PORT");
$mail->SMTPAuth = true;
$mail->Username = getenv("SMTP_USERNAME");
$mail->Password = getenv("SMTP_PASSWORD");
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->setFrom(
getenv("SMTP_FROM_ADDRESS"),
getenv("SMTP_FROM_NAME") ?: "Example App"
);
$mail->addAddress("recipient@example.com");
$mail->Subject = "Welcome";
$mail->Body = "Your account is ready.";
$mail->send();
} catch (Exception $exception) {
error_log("Mailer error: " . $mail->ErrorInfo);
throw $exception;
}
Do not echo SMTP errors directly to users. Log enough detail for operators, then return a generic application error.
Send HTML email in PHP
For HTML emails, set isHTML(true) and include a text alternative:
<?php
$mail->isHTML(true);
$mail->Subject = "Reset your password";
$mail->Body = <<<HTML
<h1>Password reset</h1>
<p>Use the secure link in this email to reset your password.</p>
HTML;
$mail->AltBody = "Use the secure link in this email to reset your password.";
$mail->send();
Test HTML messages in email client testing before broad sends. A message that looks fine in a local browser can still fail in Gmail, Outlook, or mobile clients.
Send attachments from PHP
Use deterministic file paths and check files before attaching them:
<?php
$attachments = [
__DIR__ . "/reports/invoice.pdf",
__DIR__ . "/reports/receipt.csv",
];
foreach ($attachments as $path) {
if (!is_readable($path)) {
throw new RuntimeException("Attachment not readable: {$path}");
}
$mail->addAttachment($path);
}
$mail->Subject = "Invoice and receipt";
$mail->Body = "Your invoice and receipt are attached.";
$mail->send();
After changing attachment logic, send to a MailSlurp inbox and assert that the expected filenames, content types, and attachment counts arrive.
Send email from a PHP contact form safely
A contact form should not pass raw user input straight into headers. Keep sender identity controlled, validate the recipient, and place user input in the body:
<?php
$email = filter_input(INPUT_POST, "email", FILTER_VALIDATE_EMAIL);
$message = trim((string) ($_POST["message"] ?? ""));
if (!$email || $message === "") {
http_response_code(400);
exit("Invalid form input");
}
$mail->setFrom(getenv("SMTP_FROM_ADDRESS"), "Website contact form");
$mail->addReplyTo($email);
$mail->addAddress("support@example.com");
$mail->Subject = "New contact form message";
$mail->Body = $message;
$mail->send();
Add rate limits, spam checks, and server-side validation before exposing a public send endpoint.
Send email from PHP with the MailSlurp API
When you want a testable API workflow instead of direct SMTP, send from a MailSlurp inbox:
<?php
$apiKey = getenv("MAILSLURP_API_KEY");
$inboxId = getenv("MAILSLURP_INBOX_ID");
$payload = json_encode([
"to" => ["recipient@example.com"],
"subject" => "PHP API smoke test",
"body" => "Sent from a PHP job through MailSlurp.",
], JSON_THROW_ON_ERROR);
$ch = curl_init("https://api.mailslurp.com/inboxes/{$inboxId}/confirm");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"x-api-key: {$apiKey}",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
if ($response === false) {
throw new RuntimeException(curl_error($ch));
}
curl_close($ch);
This is useful for CI smoke tests, support tooling, and scheduled jobs where an API response is easier to inspect than a raw SMTP session.
Wait for the received email
Do not stop at "send returned true". Wait for the message and assert the content:
<?php
$receiveInboxId = getenv("MAILSLURP_RECEIVE_INBOX_ID");
$url = "https://api.mailslurp.com/waitForLatestEmail"
. "?inboxId=" . rawurlencode($receiveInboxId)
. "&timeout=120000&unreadOnly=true";
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ["x-api-key: {$apiKey}"],
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
if ($response === false) {
throw new RuntimeException(curl_error($ch));
}
$email = json_decode($response, true, flags: JSON_THROW_ON_ERROR);
if (($email["subject"] ?? "") !== "PHP API smoke test") {
throw new RuntimeException("Unexpected email subject");
}
Use this pattern for signup links, OTP codes, password resets, invoice receipts, and other PHP workflows where delivery matters.
Common PHP SMTP errors and fixes
535 authentication failed
Check the SMTP username, password, tenant policy, app-password requirement, and sender identity. Then confirm your from-domain aligns with SPF, DKIM, and DMARC. Use the SMTP authentication guide for the deeper checklist.
Connection timed out
Validate host, port, TLS mode, DNS resolution, and outbound firewall rules. Many hosting environments restrict outbound SMTP until it is explicitly enabled.
SMTP accepted the message but the inbox never sees it
Treat this as a deliverability or policy problem. Check headers, SPF, DKIM, DMARC, spam score, blacklist status, and inbox placement:
- Email header analyzer
- SPF checker
- DKIM checker
- DMARC checker
- Email blacklist checker
- Email deliverability test
HTML form sends are abused
Add server-side validation, CAPTCHA or abuse controls where appropriate, rate limits, recipient allowlists, and webhook monitoring for suspicious send patterns.
Production checklist for PHP email
Before shipping PHP email changes:
- Keep SMTP and API secrets outside source code.
- Use PHPMailer SMTP or MailSlurp API for important sends.
- Confirm port, TLS, and SMTP authentication in staging and production.
- Validate sender-domain SPF, DKIM, and DMARC.
- Send to a MailSlurp inbox and assert subject, body, links, and attachments.
- Test customer-visible HTML in real email clients.
- Monitor bounces, failures, and webhook events after release.
Start with Email Sandbox for isolated tests, email integration testing for CI assertions, and email webhooks for delivery and workflow evidence.
FAQ
What is the best way to send email in PHP?
For production workflows, use PHPMailer with authenticated SMTP or an email API. Native mail() can work for simple cases but is harder to test and operate reliably.
Can PHP send HTML email?
Yes. With PHPMailer, call isHTML(true), set an HTML body, and include AltBody for clients that need plain text.
Can PHP send attachments?
Yes. PHPMailer supports addAttachment(). Check the file path before sending and verify received attachments in a test inbox.
How do I test PHP email in CI?
Send to a MailSlurp inbox, call wait-for-email, and assert subject, body, links, headers, and attachments before the CI job passes.