Cpanel中Pipe to Program邮件转发重复问题求助
问题描述
我们用Cpanel的「Pipe to Program」功能做邮件转发,转发规则如下:
| 邮箱地址 | 转发目标 |
|---|---|
| email.1@domain.com | |/home/user/forwarder/forward.php |
| email.2@domain.com | |/home/user/forwarder/forward.php |
对应的forward.php脚本逻辑:
#!/usr/bin/php -q <?php $forwarders = [ 'email.1@domain.com' => ['email.1@gmail.com'], 'email.2@domain.com' => ['email.2@gmail.com'] ]; $recipients = $parseHeader['to']; $to = []; foreach($recipients as $recipient){ if(isset($forwarders[$recipient])){ foreach($forwarders[$recipient] as $forwarder){ $to[] = $forwarder; } } } //Send one email with emails in $to
当邮件同时发送到email.1@domain.com和email.2@domain.com时,脚本生成的$to数组包含两个目标邮箱,但实际每个目标邮箱会收到2份相同邮件(3个收件人则收到3份)。
问题根源
邮件同时发给多个触发Pipe规则的邮箱时,Cpanel会为每个收件人单独触发一次脚本执行:
- 邮件发送到
email.1@domain.com和email.2@domain.com - 触发
email.1@domain.com的转发规则,脚本执行并向两个目标邮箱发邮件 - 同时触发
email.2@domain.com的转发规则,脚本再次执行并向两个目标邮箱发邮件
最终每个目标邮箱收到多次重复邮件。
解决方案
方法1:让脚本只处理当前触发它的收件人(推荐)
Cpanel调用Pipe脚本时,会把当前触发的邮箱地址通过命令行参数$argv[1]传递给脚本。直接用这个值获取对应的转发目标,不用解析所有收件人。
修改后的forward.php:
#!/usr/bin/php -q <?php $forwarders = [ 'email.1@domain.com' => ['email.1@gmail.com'], 'email.2@domain.com' => ['email.2@gmail.com'] ]; // 获取当前触发脚本的收件人(Cpanel传递的参数) $currentRecipient = $argv[1] ?? ''; $to = []; if(isset($forwarders[$currentRecipient])){ $to = $forwarders[$currentRecipient]; } // 只向当前收件人对应的目标邮箱发送邮件 if(!empty($to)){ // 执行邮件发送逻辑,比如用mail()或PHPMailer }
这样每次脚本执行只处理触发它的那个邮箱对应的转发目标,从根源避免重复发送。
方法2:通过邮件唯一标识避免重复
如果需要保留解析所有收件人的逻辑,可以利用邮件的Message-ID(每个邮件的唯一标识),记录已处理过的ID,避免重复执行发送操作。
示例代码片段:
#!/usr/bin/php -q <?php // 从标准输入读取邮件头,提取Message-ID $messageId = ''; $fd = fopen("php://stdin", "r"); while (!feof($fd)) { $line = fgets($fd); if(trim($line) === '') break; // 邮件头结束 if(strpos($line, 'Message-ID:') === 0){ $messageId = trim(str_replace('Message-ID:', '', $line)); break; } } fclose($fd); // 记录已处理Message-ID的文件路径 $processedIdsFile = '/home/user/forwarder/processed_ids.txt'; $processedIds = file_exists($processedIdsFile) ? file($processedIdsFile, FILE_IGNORE_NEW_LINES) : []; // 该邮件已处理过,直接退出 if(in_array($messageId, $processedIds)){ exit; } // 执行原有的转发逻辑 $forwarders = [ 'email.1@domain.com' => ['email.1@gmail.com'], 'email.2@domain.com' => ['email.2@gmail.com'] ]; $recipients = $parseHeader['to']; $to = []; foreach($recipients as $recipient){ if(isset($forwarders[$recipient])){ foreach($forwarders[$recipient] as $forwarder){ $to[] = $forwarder; } } } // 发送邮件 // Send one email with emails in $to // 记录已处理的Message-ID file_put_contents($processedIdsFile, $messageId . PHP_EOL, FILE_APPEND);
注意:定期清理processed_ids.txt,避免文件过大占用空间。
方法3:合并转发规则
如果多个邮箱的转发逻辑完全一致,可以把所有需要转发的邮箱合并到一个规则里:
- 创建一个统一的中转邮箱,比如
forward-all@domain.com - 把
email.1@domain.com和email.2@domain.com都转发到这个中转邮箱 - 只为中转邮箱设置Pipe规则到脚本
这样不管多少个收件人,只会触发一次脚本执行,彻底避免重复。
内容的提问来源于stack exchange,提问作者john
相关产品推荐
相关产品推荐

