You’re building a PHP web app and sooner or later you hit the “okay, now it has to send an email” moment. Password resets, signup confirmation, invoices… they all need reliable email.
In this guide we’ll walk through how to send email in PHP using SMTP, PHPMailer, and an email API, with copy‑pasteable code and a clear setup path. The whole idea is to get you from “it works on localhost” to “it’s stable in production” with good deliverability, predictable performance, and no drama.
For a smooth ride, make sure you have:
PHP 7.4+ on your server or local machine
Composer installed so you can pull in libraries
Basic PHP web development skills (functions, namespaces, error handling)
An email service provider with SMTP credentials or an API key
A text editor/IDE you’re comfortable with
In examples below, PHPMailer handles SMTP, and a PHP SDK handles the email API approach. You can plug in any serious provider you like.
PHP has a built-in mail() function, but it’s very basic and painful in production. It lacks proper authentication, good error handling, and advanced features like attachments or HTML email.
So we’ll use PHPMailer, which is the standard way to send email in PHP via SMTP.
From your project root:
bash
composer require phpmailer/phpmailer
Then in your PHP file:
php
<?php
require_once 'vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
That require_once pulls in PHPMailer via Composer’s autoloader.
Now let’s wire PHPMailer to your SMTP provider:
php
<?php
require_once 'vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
// Initialize the PHPMailer
$mail = new PHPMailer(true);
// SMTP server configuration
$mail->isSMTP();
$mail->Host = 'smtp.sendlayer.net'; // or your provider
$mail->SMTPAuth = true;
$mail->Username = 'your_smtp_username';
$mail->Password = 'your_smtp_password';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
We create a $mail instance, switch it to SMTP mode, and fill in host, port, and credentials from your provider’s dashboard.
Your code can be perfectly written, but if the server under it is slow, unstable, or constantly blacklisted, email will still feel broken to your users. When you move from local tests to production, having fast, clean infrastructure for your PHP mailer starts to matter a lot.
👉 See how GTHost can give your PHP email scripts a faster, more reliable home server
With a solid host behind you, SMTP timeouts and random slowdowns are much less of a problem, and you can focus on your code instead of babysitting servers.
Let’s send a simple text email end-to-end:
php
<?php
require_once 'vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
$mail = new PHPMailer(true);
try {
// SMTP server configuration
$mail->isSMTP();
$mail->Host = 'smtp.sendlayer.net';
$mail->SMTPAuth = true;
$mail->Username = 'your_smtp_username';
$mail->Password = 'your_smtp_password';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
// Recipients
$mail->setFrom('[email protected]', 'Your Name');
$mail->addAddress('[email protected]', 'Recipient Name');
// Content
$mail->isHTML(false); // plain text email
$mail->Subject = 'Plain Text Email Subject';
$mail->Body = 'This is a plain text email sent using PHPMailer over SMTP.';
// Send
$mail->send();
echo 'Email sent successfully!';
} catch (Exception $e) {
echo 'Email could not be sent. Mailer Error: ' . $mail->ErrorInfo;
}
Key points:
setFrom() and addAddress() set sender and recipient.
isHTML(false) means “plain text mode.”
$mail->send() triggers the actual send.
Wrapping everything in try...catch gives you real error messages instead of silent failures.
If your provider requires you to verify a sending domain (for example example.com), make sure your setFrom() email matches that domain.
HTML email is almost the same, you just flip isHTML(true) and provide HTML in Body:
php
<?php
require_once 'vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
$mail = new PHPMailer(true);
try {
// SMTP Server configuration
$mail->isSMTP();
$mail->Host = 'smtp.sendlayer.net';
$mail->SMTPAuth = true;
$mail->Username = 'your_smtp_username';
$mail->Password = 'your_smtp_password';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
// Recipients
$mail->setFrom('[email protected]', 'Your Company');
$mail->addAddress('[email protected]', 'John Doe');
// Content
$mail->isHTML(true);
$mail->Subject = 'Welcome to Our Platform!';
$mail->Body = '
<html>
<head>
<style>
.container { font-family: Arial, sans-serif; }
.header { background-color: #f4f4f4; padding: 20px; }
.content { padding: 20px; }
.button {
background-color: #007cba;
color: white;
padding: 10px 20px;
text-decoration: none;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h2>Welcome to Our Platform!</h2>
</div>
<div class="content">
<p>Hi Pattie,</p>
<p>Thank you for signing up. We\'re excited to have you on board!</p>
<p><a href="https://example.com/activate" class="button">Activate Account</a></p>
<p>Best regards,<br>The Team</p>
</div>
</div>
</body>
</html>';
// Optional plain text fallback
$mail->AltBody = 'Thank you for signing up! Visit https://example.com/activate to activate your account.';
$mail->send();
echo 'HTML email sent successfully!';
} catch (Exception $e) {
echo 'Email could not be sent. Mailer Error: ' . $mail->ErrorInfo;
}
The AltBody is handy for clients that don’t handle HTML well.
You can call addAddress() multiple times and mix in CC/BCC:
php
// Sender
$mail->setFrom('[email protected]', 'Newsletter Team');
// Multiple recipients
$mail->addAddress('[email protected]', 'John Doe');
$mail->addAddress('[email protected]', 'Jane Smith');
$mail->addCC('[email protected]', 'Manager');
$mail->addBCC('[email protected]', 'Admin');
// Content
$mail->isHTML(true);
$mail->Subject = 'Weekly Newsletter';
$mail->Body = '
Here are the latest news and updates...
';
$mail->send();
echo 'Newsletter sent to multiple recipients!';
That’s enough for classic “send one email to a bunch of people” use cases.
Attachments are one line each:
php
// Recipients
$mail->setFrom('[email protected]', 'Document Center');
$mail->addAddress('[email protected]');
// Attachments
$mail->addAttachment('/path/to/document.pdf', 'Invoice.pdf');
$mail->addAttachment('/path/to/image.jpg', 'Photo.jpg');
// Content
$mail->isHTML(true);
$mail->Subject = 'Documents Attached';
$mail->Body = '
Please find the attached documents.
';
$mail->send();
echo 'Email with attachments sent successfully!';
Just duplicate addAttachment() for each file. Paths can be absolute or relative.
Once your script is ready, test from the command line:
bash
php SendEmail.php
Make sure you’re in the project directory and SendEmail.php matches your filename. If everything’s wired correctly, you’ll see your echo message and the email should land in the test inbox.
SMTP is fine, but once you start sending more volume, or you want better speed and analytics, an email API usually feels smoother and more stable than raw SMTP.
Here we’ll use a PHP SDK (SendLayer’s style of API) as an example. The flow is similar for most providers:
Install SDK via Composer
Initialize it with an API key
Call a send() method with data like from, to, subject, text / html
In your project:
bash
composer require sendlayer/sendlayer-php
php
<?php
require_once 'vendor/autoload.php';
use SendLayer\SendLayer;
use SendLayer\Exceptions\SendLayerException;
// Initialize the SendLayer package
$sendlayer = new SendLayer('your-api-key');
Grab the API key from your provider dashboard and replace 'your-api-key'.
php
<?php
require_once 'vendor/autoload.php';
use SendLayer\SendLayer;
use SendLayer\Exceptions\SendLayerException;
// Initialize the SendLayer package
$sendlayer = new SendLayer('your-api-key');
// Call the send email method
try {
$response = $sendlayer->Emails->send([
'from' => '[email protected]',
'to' => '[email protected]',
'subject' => 'Test Email from PHP SDK',
'text' => 'This is a test email sent using the SendLayer PHP SDK'
]);
echo "Email sent successfully! " . json_encode($response, JSON_PRETTY_PRINT) . PHP_EOL;
} catch (SendLayerException $e) {
echo "Error: " . $e->getMessage() . PHP_EOL;
}
Minimal payload, clear response, and good error messages. For production, this usually behaves more predictably than raw SMTP.
Required fields:
from – sender email (usually must match a verified domain)
to – one or more recipient emails
subject – short and clear
text or html – message body
You can send to multiple addresses by passing an array:
php
$response = $sendlayer->Emails->send([
'from' => '[email protected]',
'to' => [
'[email protected]',
'[email protected]',
],
'subject' => 'Test Email from PHP SDK',
'html' => '
This is a test email sent to multiple recipients using a PHP SDK.
',
]);
echo "Email sent successfully! " . json_encode($response, JSON_PRETTY_PRINT) . PHP_EOL;
If you want names and extra fields:
php
$response = $sendlayer->Emails->send([
'from' => [
'email' => '[email protected]',
'name' => 'Paulie Paloma',
],
'to' => [
[ 'name' => 'John Doe', 'email' => '[email protected]' ],
[ 'name' => 'Pattie Paloma', 'email' => '[email protected]' ],
],
'subject' => 'Sending a Test Email Using PHP SDK',
'html' => '
This is a test email sent to multiple recipients using a PHP SDK.
',
'cc' => ['[email protected]'],
'bcc' => ['[email protected]'],
'replyTo' => '[email protected]',
]);
echo "Email sent successfully! Message ID: " . $response['MessageID'] . PHP_EOL;
That’s enough to cover most transactional email flows from your PHP web application.
Most email APIs let you attach files by giving them a path and MIME type:
php
$response = $sendlayer->Emails->send([
'from' => [
'email' => '[email protected]',
'name' => 'Paulie Paloma',
],
'to' => [
[
'name' => 'Pattie Paloma',
'email' => '[email protected]',
],
],
'subject' => 'Sending a Test Email With PHP SDK',
'html' => '
This is a test email with attachments using a PHP SDK.
',
'attachments' => [
[
'path' => '/path/to/document.pdf',
'type' => 'application/pdf',
],
[
'path' => 'https://placehold.co/600x400.png',
'type' => 'image/png',
],
],
]);
echo "PHP email sent successfully! Message ID: " . $response['MessageID'] . PHP_EOL;
The SDK takes care of encoding. You just point it at the file.
Now that sending works, let’s harden it a bit so it doesn’t bite you later.
Never trust user input, especially when it ends up in emails.
php
<?php
function validateEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
function sanitizeInput($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
return $data;
}
// Example usage
$email = $_POST['email'] ?? '';
if (!validateEmail($email)) {
die('Invalid email address');
}
$name = sanitizeInput($_POST['name'] ?? '');
$message = sanitizeInput($_POST['message'] ?? '');
This reduces weird errors and helps protect your domain reputation.
Wrap your send logic in a function and return useful info:
php
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
function sendEmail($to, $subject, $body) {
$mail = new PHPMailer(true);
try {
// SMTP configuration
$mail->isSMTP();
$mail->Host = 'smtp.sendlayer.net';
$mail->SMTPAuth = true;
$mail->Username = 'your_username';
$mail->Password = 'your_password';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
// Email settings
$mail->setFrom('[email protected]', 'Your App');
$mail->addAddress($to);
$mail->isHTML(true);
$mail->Subject = $subject;
$mail->Body = $body;
$mail->send();
return ['success' => true, 'message' => 'Email sent successfully'];
} catch (Exception $e) {
error_log('Email Error: ' . $e->getMessage());
return ['success' => false, 'message' => 'Failed to send email'];
}
}
Now your application can react to failures instead of guessing.
Never hardcode SMTP passwords or API keys in your repo. Use a .env file locally plus environment variables in production.
First install Dotenv:
bash
composer require vlucas/phpdotenv
Then load and use the variables:
php
<?php
use Dotenv\Dotenv;
// Load environment variables
$dotenv = Dotenv::createImmutable(DIR);
$dotenv->load();
// Import and map environment variables
$mail->Host = $_ENV['SMTP_HOST'];
$mail->Username = $_ENV['SMTP_USERNAME'];
$mail->Password = $_ENV['SMTP_PASSWORD'];
$mail->Port = (int) $_ENV['SMTP_PORT'];
Add .env to .gitignore so it never lands in version control.
For better deliverability:
Add SPF records so providers know who can send for your domain
Set up DKIM signatures so messages can’t be easily forged
Configure DMARC so receivers know what to do with suspicious mail
Most modern email providers walk you through this with copy‑paste DNS records. It’s a one-time setup that pays off with fewer spam-folder trips.
mail() is the built-in function. It’s simple but limited: no SMTP auth, no consistent error handling, and attachments are clunky. It also depends heavily on server configuration.
PHPMailer is a full library. It handles SMTP authentication, HTML, attachments, multiple recipients, and returns much clearer errors. For any serious send email in PHP scenario, PHPMailer is the better choice.
With PHPMailer, use addAttachment():
php
$mail->addAttachment('/path/to/file.pdf', 'Invoice.pdf');
You can call it multiple times for multiple files. The library handles the formatting.
Email is one of those quiet features in PHP web development: nobody notices when it works, everyone complains when it doesn’t. By combining PHPMailer for SMTP and an email API for higher volume, you get a setup that’s faster, more stable, and easier to debug than relying on mail() alone.
If you want your PHP email scripts to live on infrastructure that’s actually built for performance and reliable delivery, it’s worth understanding 👉 why GTHost is suitable for high-volume PHP email delivery scenarios. Pair a solid hosting base with the patterns in this guide, and sending email in PHP becomes a normal part of your stack instead of a mysterious black box.