W3docs

PHP Mail

Today, we will discuss the mail() function in PHP. This function is used to send email messages from a PHP script.

PHP's built-in mail() function sends email straight from a script — no extra library required. It is the quickest way to fire off a notification, a "contact us" message, or a password-reset link. This chapter explains the function's signature, walks through plain-text and HTML examples, and covers the security and deliverability pitfalls that trip people up in production.

What the mail() Function Does

mail() hands a message to the server's local mail transfer agent (MTA) — the program (such as Sendmail, Postfix, or Exim) that actually relays mail to the outside world. PHP does not talk to remote SMTP servers itself; it only queues the message locally. If the host has no working MTA, mail() returns false and nothing is sent.

The function returns a boolean: true if the message was accepted for delivery (not the same as delivered), and false on failure.

Syntax

mail(
    string $to,
    string $subject,
    string $message,
    array|string $additional_headers = [],
    string $additional_params = ""
): bool
ParameterRequiredDescription
$toYesRecipient address(es). Separate multiple addresses with a comma.
$subjectYesSubject line. Must not contain newlines.
$messageYesThe email body. Lines should be separated by \r\n and no longer than 70 characters.
$additional_headersNoExtra headers such as From, Reply-To, or Content-Type. As of PHP 7.2 this can be an associative array.
$additional_paramsNoExtra command-line flags passed to the MTA (for example, the envelope sender via -f).

Sending a Plain-Text Email

This is the most common case: a simple text message with a few headers.

<?php
$to      = '[email protected]';
$subject = 'Test email';
$message = 'This is a test email from PHP.';

// Additional headers
$headers  = 'From: [email protected]' . "\r\n";
$headers .= 'Reply-To: [email protected]' . "\r\n";
$headers .= 'X-Mailer: PHP/' . phpversion();

if (mail($to, $subject, $message, $headers)) {
    echo 'Email sent successfully.';
} else {
    echo 'Failed to send email.';
}
?>

We set the recipient, subject, and body, then build a headers string. The From header identifies the sender, Reply-To controls where replies go, and X-Mailer is informational. Headers are joined with \r\n (carriage return + line feed), which is the line ending the email protocol requires.

Sending an HTML Email

To send formatted HTML, add Content-type: text/html to the headers. Without it, the recipient sees raw tags instead of rendered markup.

<?php
$to      = '[email protected]';
$subject = 'HTML email';
$message = '
<html>
<body>
    <h1>Hello!</h1>
    <p>This message is <strong>formatted with HTML</strong>.</p>
</body>
</html>';

// To send HTML mail, the Content-type header must be set
$headers  = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/html; charset=UTF-8' . "\r\n";
$headers .= 'From: [email protected]' . "\r\n";

mail($to, $subject, $message, $headers);
?>

Sending to Multiple Recipients

Pass a comma-separated list in $to, or use Cc/Bcc headers:

<?php
$to      = '[email protected], [email protected]';
$subject = 'Team update';
$message = 'Meeting moved to 3 PM.';

$headers  = 'From: [email protected]' . "\r\n";
$headers .= 'Bcc: [email protected]' . "\r\n";

mail($to, $subject, $message, $headers);
?>

Guard Against Header Injection

If any part of a header — especially From or Reply-To — comes from user input (such as a contact form), an attacker can inject extra newlines to add their own headers and turn your script into a spam relay. Never put raw user input into headers. Validate and sanitize first:

<?php
// $_POST['email'] comes from an untrusted form
$email = filter_var($_POST['email'] ?? '', FILTER_VALIDATE_EMAIL);

if ($email === false) {
    exit('Invalid email address.');
}

// Strip any line breaks that could be used to inject headers
$email = str_replace(["\r", "\n"], '', $email);

$headers = 'From: ' . $email . "\r\n";
mail('[email protected]', 'Contact form', 'New message', $headers);
?>

See filter_var() and the PHP Filter chapter for more on validating input, and PHP Form Validation for handling form data safely.

Deliverability and Production Notes

Note: The mail() function relies on the server's local MTA. On many modern hosting environments it fails silently or lands in spam without proper configuration. For real applications, prefer an established library such as PHPMailer or Symfony Mailer, which support SMTP authentication, attachments, encoding, and queueing. Whichever route you choose, give your domain valid SPF, DKIM, and DMARC records so mailbox providers trust your messages.

A returned true only means the MTA accepted the message — it is not a delivery confirmation. To track actual delivery, bounces, and opens, use a transactional email service.

Common Use Cases

  • Contact forms — collect a message and email it to the site owner (combine with PHP Form Handling).
  • Notifications — order confirmations, alerts, password resets.
  • Reports — scheduled scripts that email a summary.

Conclusion

PHP's mail() function offers a quick, dependency-free way to send email from a script: supply the recipient, subject, and body, then add headers for the sender, reply address, or HTML content. Keep its limitations in mind — it depends on a configured MTA, returns acceptance rather than delivery, and must never receive unsanitized user input in its headers. For anything beyond simple, low-volume mail, reach for a dedicated library. To keep building, explore PHP Functions and PHP Strings.

Practice

Practice
What are the parameters of the PHP mail() function?
What are the parameters of the PHP mail() function?
Was this page helpful?