If your PHP mail() function is failing, the error is usually one of three things: the mail server isn’t reachable, PHP isn’t configured with the right sendmail path, or the message itself is being rejected. This guide walks you through the most common causes and gives you step-by-step fixes to get your emails sending again.
How PHP mail() Actually Works
PHP’s mail() function doesn’t send email itself. It hands the message to a local mail transfer agent (MTA) — on Linux that’s usually sendmail or postfix; on Windows it’s an SMTP server you configure in php.ini. The MTA then delivers the message to the recipient’s mail server. So when mail() returns false, it almost always means the handoff to the MTA failed, not that the email was rejected by the recipient.
Step 1: Check If mail() Returns True or False
The simplest diagnostic is to capture the return value. Add this to a test script:
<?php
$ok = mail('you@example.com', 'Test', 'Hello');
var_dump($ok);
?>
If it prints bool(false), PHP couldn’t hand off to the MTA. If it prints bool(true), the MTA accepted the message — but that doesn’t guarantee delivery. Check your spam folder and the server’s mail log (/var/log/mail.log on most Linux systems).
Step 2: Verify the sendmail Path in php.ini
On Linux, PHP needs to know where the sendmail binary is. Open your php.ini and look for:
sendmail_path = /usr/sbin/sendmail -t -i
If it’s commented out or empty, PHP can’t send. Run which sendmail or which postfix to find the correct path. On a typical Debian/Ubuntu system, it’s /usr/sbin/sendmail. On CentOS, it might be /usr/sbin/sendmail.sendmail. Update the path, restart your web server (Apache: sudo systemctl restart apache2; Nginx with PHP-FPM: sudo systemctl restart php-fpm), and test again.
Step 3: Confirm the MTA Is Running
Even with the correct path, if the MTA service is down, mail() will fail. Check with:
sudo systemctl status sendmail sudo systemctl status postfix
If it’s not running, start it: sudo systemctl start sendmail (or postfix). On some minimal cloud images, no MTA is installed at all. Install one with sudo apt install postfix (Debian/Ubuntu) or sudo yum install postfix (CentOS). During install, choose “Internet Site” and set the system mail name to your domain.
Step 4: Look at the PHP Error Log
When mail() fails, PHP often writes a warning to its error log. Find your log location by running:
php -i | grep error_log
Common entries include:
sendmail: Invalid argument— usually means thesendmail_pathis wrong or the binary doesn’t exist.Permission denied— the web server user (e.g.,www-data) can’t execute the sendmail binary.Connection refused— on Windows, the SMTP server isn’t reachable.
Step 5: Test the MTA Directly
To isolate whether the problem is PHP or the MTA, try sending from the command line:
echo "Test" | sendmail -t -i you@example.com
If that works, the MTA is fine and the issue is PHP’s configuration. If it fails, you’ll see the MTA’s own error message — often it can’t resolve the recipient’s domain or the server’s hostname isn’t set correctly. Fix the hostname with sudo hostnamectl set-hostname mail.yourdomain.com and ensure DNS resolves.
Step 6: Check for Firewall and SMTP Port Issues
On Windows, PHP’s mail() uses the SMTP settings in php.ini: SMTP (server) and smtp_port (usually 25). If you’re using a remote SMTP server, verify that port 25 (or 587 for submission) is open outbound. Many cloud providers block port 25 by default. Test with telnet smtp.example.com 25 — if it connects, the port is open. If not, you may need to use port 587 with authentication, which the built-in mail() doesn’t support. In that case, you’ll need a library like PHPMailer or SwiftMailer.
Step 7: Inspect the Message Headers
Sometimes mail() returns true but the email never arrives. This is often because the headers are malformed. Always set a From header; without it, many servers reject the mail. Use a valid domain that matches your server’s hostname:
$headers = 'From: webmaster@yourdomain.com' . "\r\n" .
'Reply-To: webmaster@yourdomain.com' . "\r\n";
mail('recipient@example.com', 'Subject', 'Body', $headers);
Also check that the recipient address is correct and that your domain’s SPF record allows your server to send. If SPF fails, the receiving server will likely drop the message.
Common Pitfalls and Misconceptions
- mail() returns true but no email arrives — The MTA accepted it, but the recipient’s server rejected it (spam, bad SPF, or the server IP is blacklisted). Check the mail log for bounce messages.
- Using mail() on localhost — On a development machine, there’s often no MTA installed. Install one or use a tool like Mailhog to capture emails locally.
- Headers injection — If you pass user input to headers without sanitizing, attackers can inject extra headers. Always filter newlines from input.
- Windows vs Linux — Windows requires an SMTP server setting; Linux requires a sendmail path. They’re not interchangeable.
Alternatives to mail()
If you keep hitting walls, consider using a library that speaks SMTP directly. PHPMailer and SwiftMailer are the most common. They support authentication, TLS, and attachments, and they give you much clearer error messages. For example, with PHPMailer, you can see exactly why a connection failed. Many developers prefer this over the built-in function because it’s more reliable and easier to debug.
Frequently Asked Questions
Why does mail() return true but the email never arrives?
This is the most confusing scenario. mail() only confirms that the local MTA accepted the message. The MTA then tries to deliver it, but the receiving server may reject it for reasons like missing SPF, a blacklisted IP, or the message being flagged as spam. Check the mail log on your server for a bounce message that explains the rejection.
How do I set the From address in PHP mail()?
You must include a From header in the fourth argument of mail(). If you don’t, the server may use a default like www-data@localhost, which many recipients reject. Use a real domain that you control, and ideally set up SPF and DKIM for that domain.
What does “sendmail: Invalid argument” mean?
This warning typically appears when the sendmail_path in php.ini points to a non-existent binary or includes flags that the binary doesn’t understand. Double-check the path with which sendmail and make sure the flags are exactly -t -i.
Can I use Gmail SMTP with PHP mail()?
Not directly. The built-in mail() doesn’t support SMTP authentication. You’d need to use PHPMailer or SwiftMailer, which can authenticate with Gmail’s SMTP server on port 587 with TLS. Gmail also requires an app password if you have two-factor authentication enabled.
Why is mail() failing on my local Windows machine?
On Windows, PHP’s mail() relies on the SMTP and smtp_port settings in php.ini. By default, it tries to connect to localhost on port 25, which usually isn’t running. You can install a local SMTP server like hMailServer, or use a tool like Mailhog to capture emails for testing.
How do I fix “Permission denied” when calling mail()?
This means the web server user (e.g., www-data on Apache) doesn’t have execute permission on the sendmail binary. Check the permissions with ls -l /usr/sbin/sendmail. If it’s not executable by others, you can add the web server user to the appropriate group or adjust the permissions with sudo chmod 755 /usr/sbin/sendmail.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Blockchain vs Database: When Does Distributed Trust Win?
- GTA 6 and Call of Duty on Same Console?
- MCP in Production: Security Failures Behind the Backdoor Label
- Nothing Phone 2 Waterproof? IP54 Rating Explained
- Satellite Internet Equipment & Installation Guide
- Infinix GT 20 Pro vs AI Nova 2 Ultra 5G: Which Fits?