PHP邮件功能无法完成电子邮件发送

562
<?php
    $name = $_POST['name'];
    $email = $_POST['email'];
    $message = $_POST['message'];
    $from = 'From: yoursite.com';
    $to = 'contact@yoursite.com';
    $subject = 'Customer Inquiry';
    $body = "From: $name\n E-Mail: $email\n Message:\n $message";

    if ($_POST['submit']) {
        if (mail ($to, $subject, $body, $from)) {
            echo '<p>Your message has been sent!</p>';
        } else {
            echo '<p>Something went wrong, go back and try again!</p>';
        }
    }
?>
我尝试创建一个简单的邮件表单。表单本身位于我的index.html页面上,但是它提交到一个单独的“谢谢您的提交”页面thankyou.php,在那里嵌入了上面的PHP代码。 代码可以成功提交,但从未发送电子邮件。我该如何解决这个问题?

3
您的程序在本地计算机还是网络主机上运行? - Tamil Selvan C
9
  1. 你确定它没有进入你的垃圾邮件文件夹吗?
  2. 你的计算机上配置了sendmail吗?
- Steven Moseley
它在一个实时主机上。它没有进入我的垃圾邮件...我不确定你所说的配置sendmail是否在我的盒子上,所以我假设没有? - user3818620
1
尝试发送而不使用 $from - Artem Ilchenko
我之前遇到过类似的问题,但它并不是由下面推荐答案中列出的任何错误引起的。这是因为我的 DNS 列出了另一个服务器作为授权邮件服务器,但 Web 服务器本身也配置为将自己识别为授权邮件服务器。因此,当 PHP mail() 发送电子邮件到自己的域时,它不会检查 DNS 记录以找到正确的邮件服务器,而只是将电子邮件转发给自己。我写了一篇关于这个问题的文章,详细介绍了解决方法:https://blog.terresquall.com/2021/04/php-mail-fails-send-emails-to-own-domain-email-addresses/ - John Doe
32个回答

599

虽然此答案的某些部分仅适用于mail()函数本身的用法,但这些故障排除步骤可以应用于任何PHP邮件系统。

您的脚本似乎无法发送电子邮件有很多原因。除非存在明显的语法错误,否则很难诊断这些问题。如果没有语法错误,则需要运行以下检查清单以查找可能遇到的任何潜在问题。

确保启用错误报告并设置为报告所有错误

错误报告对于查找代码中的错误和PHP遇到的一般错误至关重要。需要启用错误报告才能接收这些错误。将以下代码放置在您的PHP文件顶部(或主配置文件中)将启用错误报告。

error_reporting(-1);
ini_set('display_errors', 'On');
set_error_handler("var_dump");

请参阅 如何在PHP中获取有用的错误消息? - 本答案,了解更多相关信息。

确保正确调用mail()函数

这可能看起来很愚蠢,但常见的错误是忘记在代码中实际放置mail()函数。确保它存在并且未被注释掉。

确保正确调用mail()函数

bool mail ( string $to, string $subject, string $message [, string $additional_headers [, string $additional_parameters ]] )

mail函数需要三个必需参数和可选的第四个和第五个参数。如果您对mail()的调用没有至少三个参数,则会失败。

如果mail()的调用没有按正确顺序使用正确的参数,则也会失败。

检查服务器的邮件日志

你的Web服务器应该记录所有尝试通过它发送电子邮件的操作。这些日志的位置会有所不同(您可能需要询问您的服务器管理员),但通常可以在用户的根目录下的logs文件夹中找到。其中将包含与您发送电子邮件相关的任何服务器报告的错误消息。
检查端口连接失败
端口屏蔽是大多数开发人员在使用SMTP发送电子邮件时遇到的常见问题。这可以很容易地在服务器邮件日志中跟踪(邮件日志的位置因服务器而异,如上所述)。如果您在共享托管服务器上,则默认情况下会阻止25号和587号端口。您的托管提供商有意进行此阻止。即使对于一些专用服务器也是如此。当这些端口被阻止时,请尝试使用2525号端口进行连接。如果您发现该端口也被阻止,则唯一的解决方案是联系您的托管提供商以解除对这些端口的阻止。
大多数托管提供商都会阻止这些电子邮件端口,以保护其网络免受发送垃圾邮件的影响。
使用25号或587号端口进行普通/TLS连接,使用465号端口进行SSL连接。对于大多数用户,建议使用587号端口以避免某些托管提供商设置的速率限制。

不要使用错误抑制运算符

在 PHP 中,如果在表达式前添加错误抑制运算符@,那么该表达式可能产生的任何错误消息都将被忽略。有些情况下使用此运算符是必要的,但发送邮件则不是其中之一。

如果您的代码包含@mail(...),那么您可能正在隐藏重要的错误消息,这些错误消息将帮助您进行调试。请删除@并查看是否报告了任何错误。

仅当您随后使用error_get_last()检查具体失败时才建议使用它。

检查mail()返回值

mail()函数:

如果邮件成功被接收并投递,则返回TRUE,否则返回FALSE。需要注意的是,即使邮件已经被接收并投递,也不意味着邮件一定会到达预期的目的地。
这很重要,因为:
- 如果您收到FALSE的返回值,则知道错误出现在您的服务器接受您的邮件上。这可能不是编程问题,而是服务器配置问题。您需要与系统管理员联系以找出原因。 - 如果您收到TRUE的返回值,并不意味着您的电子邮件一定会被发送。它只是表示PHP将电子邮件成功地发送到服务器上的相应处理程序。仍然有更多的失败点在PHP控制之外,可能导致电子邮件无法发送。
因此,FALSE将帮助指引您正确的方向,而TRUE并不一定意味着您的电子邮件已成功发送。这很重要!
请确保您的托管提供商允许您发送电子邮件,并且不限制邮件发送。
许多共享网络主机,特别是免费的网络托管提供商,要么不允许从他们的服务器发送电子邮件,要么限制在任何给定时间段内可以发送的数量。这是由于他们努力限制垃圾邮件发送者利用他们更便宜的服务。
如果您认为您的主机有电子邮件限制或阻止发送电子邮件,请查看他们的FAQ以查看是否列出了任何此类限制。否则,您可能需要联系他们的支持人员以验证是否存在任何关于发送电子邮件的限制。
检查垃圾邮件文件夹;防止电子邮件被标记为垃圾邮件
通常,由于各种原因,通过PHP(和其他服务器端编程语言)发送的电子邮件最终会进入收件人的垃圾邮件文件夹。在排除代码故障之前,请始终检查该文件夹。
为避免通过PHP发送的邮件被发送到收件人的垃圾邮件文件夹中,您可以在您的PHP代码和其他方面采取各种措施,以最小化将您的电子邮件标记为垃圾邮件的机会。Michiel de Mare提供的好建议包括:
请使用电子邮件验证方法,例如 SPFDKIM,以证明您的电子邮件和您的域名属于同一实体,并防止欺诈行为。 SPF 网站提供了生成站点 DNS 信息的向导。 检查您的反向DNS,确保您的邮件服务器的 IP 地址指向您用于发送邮件的域名。
确保您使用的 IP 地址未被列入黑名单
确保答复地址是有效的、存在的地址。
在收件人字段中使用完整的真实姓名,而不仅仅是电子邮件地址(例如“John Smith”<john@blacksmiths-international.com>)。
监控您的滥用账户,例如 abuse@yourdomain.examplepostmaster@yourdomain.example。这意味着 - 确保这些账户存在,阅读发送到它们的内容,并对投诉作出回应。
最后,请确保取消订阅非常容易。否则,您的用户将通过点击垃圾邮件按钮来取消订阅,这会影响您的声誉。

请参阅如何确保通过编程发送的电子邮件不会被自动标记为垃圾邮件?了解更多相关信息。

确保提供所有邮件头

一些垃圾邮件软件会拒绝缺少常见邮件头,如“发件人”和“回复地址”的邮件:

$headers = array("From: from@example.com",
    "Reply-To: replyto@example.com",
    "X-Mailer: PHP/" . PHP_VERSION
);
$headers = implode("\r\n", $headers);
mail($to, $subject, $message, $headers);

确保邮件头没有语法错误

无效的邮件头和没有邮件头一样糟糕。一个不正确的字符可能足以使您的电子邮件出现故障。请仔细检查您的语法是否正确,因为PHP不会为您捕获这些错误。

$headers = array("From from@example.com", // missing colon
    "Reply To: replyto@example.com",      // missing hyphen
    "X-Mailer: "PHP"/" . PHP_VERSION      // bad quotes
);

不要使用伪造的From:发送者

虽然邮件必须有一个发件人,但您不能随意使用任何值。特别是用户提供的发件人地址是被阻止邮件的一种可靠方法:

$headers = array("From: $_POST[contactform_sender_email]"); // No!

原因:您的网站或发送邮件服务器未被SPF / DKIM白名单列入@ hotmail或@gmail地址的负责人。它甚至可能会静默丢弃未配置From:发送者域的邮件。

确保收件人值正确

有时问题就像电子邮件的接收者值不正确那么简单。这可能是由于使用了错误的变量导致的。
$to = 'user@example.com';
// other variables ....
mail($recipient, $subject, $message, $headers); // $recipient should be $to

另一种测试方法是将收件人的值硬编码到mail()函数调用中:

mail('user@example.com', $subject, $message, $headers);

这适用于所有的mail()参数。

发送到多个帐户

为了排除电子邮件账户问题,请将您的电子邮件发送至不同电子邮件提供商的多个电子邮件帐户。如果您的电子邮件未到达用户的Gmail帐户,请将相同的电子邮件发送到Yahoo帐户、Hotmail帐户和常规POP3帐户(例如由您的ISP提供的电子邮件帐户)。
如果电子邮件到达所有或某些其他电子邮件帐户,则表明您的代码正在发送电子邮件,但电子邮件帐户提供商可能因某种原因阻止它们。如果电子邮件没有到达任何电子邮件帐户,则问题更可能与您的代码有关。

确保代码与表单方法匹配

如果您将表单方法设置为POST,请确保使用$_POST查找您的表单值。如果您将其设置为GET或根本未设置,则请确保使用$_GET查找您的表单值。

确保表单action值指向正确的位置

请确保您的表单action属性包含指向您的PHP邮件代码的值。

<form action="send_email.php" method="POST">

确保Web主机支持发送电子邮件

一些Web主机提供商不允许或启用通过其服务器发送电子邮件。原因可能各不相同,但如果他们禁用了邮件发送,则需要使用第三方方法来替代发送这些电子邮件。

在访问在线支持或FAQ后,给他们的技术支持发送一封电子邮件应该可以澄清服务器上是否可用电子邮件功能。

确保localhost邮件服务器已配置

如果您正在使用WAMP、MAMP或XAMPP在本地工作站上开发,则可能没有安装电子邮件服务器。在没有电子邮件服务器的情况下,默认情况下PHP无法发送邮件。

您可以通过安装基本的邮件服务器来解决此问题。对于Windows用户,您可以使用免费的Mercury Mail

您还可以使用SMTP发送电子邮件。请参见Vikas Dwivedithis great answer以了解如何执行此操作。

启用PHP的自定义mail.log

除了您的MTA和PHP日志文件外,您还可以专门启用mail()函数的记录。它不记录完整的SMTP交互,但至少记录函数调用参数和调用脚本。

ini_set("mail.log", "/tmp/mail.log");
ini_set("mail.add_x_header", TRUE);

请参阅http://php.net/manual/en/mail.configuration.php以了解详情。(最好在php.ini.user.ini.htaccess中启用这些选项。)

使用邮件测试服务进行检查

有各种递送和垃圾邮件检测服务可供您测试MTA / webserver设置。通常,您发送一个邮件探针到他们的地址,然后稍后获取递送报告和更具体的故障或分析:

使用不同的邮件发送器

PHP内置的mail()函数很方便,通常可以完成工作,但它存在缺陷。幸运的是,有一些替代方案提供更多的功能和灵活性,包括处理上述问题中的许多问题:

所有这些都可以与专业的SMTP服务器/服务提供商结合使用。(因为典型的08/15共享Web托管计划在电子邮件设置/可配置性方面时好时坏。)


46

在邮件功能中添加邮件头:

$header = "From: noreply@example.com\r\n";
$header.= "MIME-Version: 1.0\r\n";
$header.= "Content-Type: text/html; charset=ISO-8859-1\r\n";
$header.= "X-Priority: 1\r\n";

$status = mail($to, $subject, $message, $header);

if($status)
{
    echo '<p>Your mail has been sent!</p>';
} else {
    echo '<p>Something went wrong. Please try again!</p>';
}

27
  1. 始终尝试在邮件功能中发送头部信息。
  2. 如果您通过本地主机发送电子邮件,则需要进行SMTP设置以发送邮件。
  3. 如果您通过服务器发送电子邮件,则需检查您的服务器上是否启用了电子邮件发送功能。

25
如果您正在使用SMTP配置发送电子邮件,请尝试改用PHPMailer。您可以从https://github.com/PHPMailer/PHPMailer下载该库。
我是这样创建我的电子邮件的:
function send_mail($email, $recipient_name, $message='')
{
    require("phpmailer/class.phpmailer.php");

    $mail = new PHPMailer();

    $mail->CharSet = "utf-8";
    $mail->IsSMTP();                                      // Set mailer to use SMTP
    $mail->Host = "mail.example.com";  // Specify main and backup server
    $mail->SMTPAuth = true;     // Turn on SMTP authentication
    $mail->Username = "myusername";  // SMTP username
    $mail->Password = "p@ssw0rd"; // SMTP password

    $mail->From = "me@walalang.com";
    $mail->FromName = "System-Ad";
    $mail->AddAddress($email, $recipient_name);

    $mail->WordWrap = 50;                                 // Set word wrap to 50 characters
    $mail->IsHTML(true);                                  // Set email format to HTML (true) or plain text (false)

    $mail->Subject = "This is a Sampleenter code here Email";
    $mail->Body    = $message;
    $mail->AltBody = "This is the body in plain text for non-HTML mail clients";
    $mail->AddEmbeddedImage('images/logo.png', 'logo', 'logo.png');
    $mail->addAttachment('files/file.xlsx');

    if(!$mail->Send())
    {
       echo "Message could not be sent. <p>";
       echo "Mailer Error: " . $mail->ErrorInfo;
       exit;
    }

    echo "Message has been sent";
}

2
我还必须使用:require("phpmailer/class.smtp.php"); - Doug

19

发送邮件前只需添加一些标题:

<?php 
$name = $_POST['name'];
$email = $_POST['email'];
$message = $_POST['message'];
$from = 'From: yoursite.com'; 
$to = 'contact@yoursite.com'; 
$subject = 'Customer Inquiry';
$body = "From: $name\n E-Mail: $email\n Message:\n $message";

$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-type: text/html\r\n";
$headers .= 'From: from@example.com' . "\r\n" .
'Reply-To: reply@example.com' . "\r\n" .
'X-Mailer: PHP/' . phpversion();

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

还有一件事。在本地主机中mail()函数无法工作。请将您的代码上传到服务器并尝试。


1
这实际上使表单提交了 - 我现在的问题是...既然我已经用标题替换了它,那么如何显示用户输入的电子邮件? - user3818620
它会防止邮件被归类为垃圾邮件吗?我看到如果我们使用你的代码中的方法发送超过10封邮件,Gmail就会将这些邮件发送到垃圾邮件文件夹。 - jewelhuq
OP有这行代码:$from = 'From: yoursite.com'; 这看起来不像是一个完整的有效电子邮件地址,因为没有本地部分。您已经替换了发件人头并添加了其他头信息。或者说,在您的示例中,上面的$from是多余的。而且,由于格式不正确,这可能是OP最初的问题所在。 - Progrock

17

我在000webhost上通过以下步骤使它工作:

$headers  = "MIME-Version: 1.0" . "\r\n";
$headers .= "Content-type: text/html; charset=iso-8859-1" . "\r\n";
$headers .= "From: ". $from. "\r\n";
$headers .= "Reply-To: ". $from. "\r\n";
$headers .= "X-Mailer: PHP/" . phpversion();
$headers .= "X-Priority: 1" . "\r\n";

发送电子邮件时,直接输入电子邮件地址:

mail('email@gmail.com', $subject, $message, $headers)

使用''而不是""

这段代码可以工作,但是邮件会有半小时的延迟。


12

大多数情况下,在共享主机中mail()函数是被禁用的。 更好的选择是使用SMTP。最好的选择是Gmail或SendGrid。


SMTPconfig.php

<?php 
    $SmtpServer="smtp.*.*";
    $SmtpPort="2525"; //default
    $SmtpUser="***";
    $SmtpPass="***";
?>

SMTPmail.php

<?php
class SMTPClient
{

    function SMTPClient ($SmtpServer, $SmtpPort, $SmtpUser, $SmtpPass, $from, $to, $subject, $body)
    {

        $this->SmtpServer = $SmtpServer;
        $this->SmtpUser = base64_encode ($SmtpUser);
        $this->SmtpPass = base64_encode ($SmtpPass);
        $this->from = $from;
        $this->to = $to;
        $this->subject = $subject;
        $this->body = $body;

        if ($SmtpPort == "") 
        {
            $this->PortSMTP = 25;
        }
        else
        {
            $this->PortSMTP = $SmtpPort;
        }
    }

    function SendMail ()
    {
        $newLine = "\r\n";
        $headers = "MIME-Version: 1.0" . $newLine;  
        $headers .= "Content-type: text/html; charset=iso-8859-1" . $newLine;  

        if ($SMTPIN = fsockopen ($this->SmtpServer, $this->PortSMTP)) 
        {
            fputs ($SMTPIN, "EHLO ".$HTTP_HOST."\r\n"); 
            $talk["hello"] = fgets ( $SMTPIN, 1024 ); 
            fputs($SMTPIN, "auth login\r\n");
            $talk["res"]=fgets($SMTPIN,1024);
            fputs($SMTPIN, $this->SmtpUser."\r\n");
            $talk["user"]=fgets($SMTPIN,1024);
            fputs($SMTPIN, $this->SmtpPass."\r\n");
            $talk["pass"]=fgets($SMTPIN,256);
            fputs ($SMTPIN, "MAIL FROM: <".$this->from.">\r\n"); 
            $talk["From"] = fgets ( $SMTPIN, 1024 ); 
            fputs ($SMTPIN, "RCPT TO: <".$this->to.">\r\n"); 
            $talk["To"] = fgets ($SMTPIN, 1024); 
            fputs($SMTPIN, "DATA\r\n");
            $talk["data"]=fgets( $SMTPIN,1024 );
            fputs($SMTPIN, "To: <".$this->to.">\r\nFrom: <".$this->from.">\r\n".$headers."\n\nSubject:".$this->subject."\r\n\r\n\r\n".$this->body."\r\n.\r\n");
            $talk["send"]=fgets($SMTPIN,256);
            //CLOSE CONNECTION AND EXIT ... 
            fputs ($SMTPIN, "QUIT\r\n"); 
            fclose($SMTPIN); 
            // 
        } 
        return $talk;
    } 
}
?>

contact_email.php

<?php 
include('SMTPconfig.php');
include('SMTPmail.php');
if($_SERVER["REQUEST_METHOD"] == "POST")
{
    $to = "";
    $from = $_POST['email'];
    $subject = "Enquiry";
    $body = $_POST['name'].'</br>'.$_POST['companyName'].'</br>'.$_POST['tel'].'</br>'.'<hr />'.$_POST['message'];
    $SMTPMail = new SMTPClient ($SmtpServer, $SmtpPort, $SmtpUser, $SmtpPass, $from, $to, $subject, $body);
    $SMTPChat = $SMTPMail->SendMail();
}
?>

在服务器上以明文形式保存SMTP用户和密码是一个好主意吗? - Juri
@Juri 可能不行... 天啊,我无法弄清楚如何使邮件功能工作 :.( - user2230470

11
如果您只使用mail()函数,则需要完成配置文件。
您需要打开邮件扩展,设置SMTP smtp_port等,最重要的是您的用户名和密码。如果没有这些信息,邮件将无法发送。另外,您还可以使用PHPMail类进行发送。

11

分别尝试这两件事并结合起来尝试:

  1. 删除 if($_POST['submit']){}
  2. 移除 $from (只是我的直觉)

10

我认为这样就可以了。我只是添加了一个if(isset,并在正文中对变量进行串联,以将PHP与HTML分开。

<?php
    $name = $_POST['name'];
    $email = $_POST['email'];
    $message = $_POST['message'];
    $from = 'From: yoursite.com'; 
    $to = 'contact@yoursite.com'; 
    $subject = 'Customer Inquiry';
    $body = "From:" .$name."\r\n E-Mail:" .$email."\r\n Message:\r\n" .$message;

if (isset($_POST['submit'])) 
{
    if (mail ($to, $subject, $body, $from)) 
    { 
        echo '<p>Your message has been sent!</p>';
    } 
    else 
    { 
        echo '<p>Something went wrong, go back and try again!</p>'; 
    }
}

?>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接