When using PHP's mail()
function with Postfix, you might encounter a situation where emails destined for domains hosted on your server (but externally handled) get rejected because Postfix attempts local delivery. Here's how to properly configure Postfix to relay these messages externally.
By default, Postfix treats domains listed in mydestination
as local domains. For a domain like example.com
where:
1. The domain is registered on your server
2. MX records point to external mail servers
3. You want to send mail to addresses@example.com
Postfix will attempt local delivery unless specifically configured otherwise.
Method 1: Disable Local Mail Service (Plesk Specific)
For Plesk servers, the cleanest solution is:
/usr/local/psa/bin/domain --update example.com -mail_service false
This tells Plesk to stop treating the domain as locally hosted for mail purposes.
Method 2: Postfix Configuration Adjustments
For non-Plesk servers or more control:
# Edit main.cf
sudo nano /etc/postfix/main.cf
# Remove domain from mydestination
mydestination = localhost, localhost.localdomain
# Add transport mapping
transport_maps = hash:/etc/postfix/transport
# Create/update transport file
sudo nano /etc/postfix/transport
example.com smtp:[external.mail.server]
Then compile and reload:
sudo postmap /etc/postfix/transport
sudo systemctl reload postfix
Verify with:
echo "Test email" | mail -s "Postfix Relay Test" user@example.com
tail -f /var/log/mail.log
You should see Postfix attempting to relay rather than deliver locally.
Your PHP code doesn't need modification, but ensure proper headers:
$to = 'user@example.com';
$subject = 'Test Email';
$message = 'This should relay externally';
$headers = 'From: webmaster@yourserver.com' . "\r\n" .
'Reply-To: no-reply@yourserver.com' . "\r\n" .
'X-Mailer: PHP/' . phpversion();
mail($to, $subject, $message, $headers);
- Forgetting to
postmap
after transport file changes - DNS caching - changes might take time to propagate
- Firewall blocking outbound SMTP (port 25)
Recently, I encountered an interesting mail routing issue where PHP's mail()
function was failing to deliver messages to external domains properly. The specific case involved:
- A PHP form sending to a domain registered on my server
- Mail handling actually configured on a different mail server
- Postfix attempting local delivery first
- Message rejection when local delivery failed
By default, Postfix handles mail delivery in this order:
1. Check local domains (mydestination)
2. Check virtual alias domains
3. Check virtual mailbox domains
4. Relay to SMTP if none of above match
The key is to configure Postfix to recognize that this particular domain should be treated as external. Here are two methods:
Method 1: Disable Mail Service for the Domain
For Plesk systems (as in the original case):
/usr/local/psa/bin/domain --update example.com -mail_service false
This tells the control panel to exclude the domain from local mail handling.
Method 2: Postfix Configuration Adjustments
For direct Postfix configuration:
- Edit
/etc/postfix/main.cf
- Modify the
mydestination
parameter
# Remove the problematic domain from mydestination
mydestination = $myhostname, localhost.$mydomain, localhost
# Alternatively, use relay_domains for more control
relay_domains = hash:/etc/postfix/relay_domains
Then create /etc/postfix/relay_domains
:
example.com RELAY
When using PHP's mail function, ensure proper headers:
$to = 'user@external-domain.com';
$subject = 'Test Subject';
$message = 'Test message body';
$headers = 'From: webmaster@yourdomain.com' . "\r\n" .
'Reply-To: webmaster@yourdomain.com' . "\r\n" .
'X-Mailer: PHP/' . phpversion();
mail($to, $subject, $message, $headers);
After making changes, always:
sudo postfix reload
sudo tail -f /var/log/mail.log
Send a test message and verify it's being relayed externally rather than attempting local delivery.
For more control, consider bypassing Postfix's local delivery entirely and using SMTP directly from PHP:
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'your.smtp.server';
$mail->SMTPAuth = true;
$mail->Username = 'username';
$mail->Password = 'password';
$mail->Port = 587;
$mail->setFrom('from@example.com');
$mail->addAddress('to@external.com');
$mail->Subject = 'Subject';
$mail->Body = 'Message body';
$mail->send();