什么是退信地址?
退信地址也称 Return-Path 或信封发件人,是在邮件无法投递时接收非投递报告(NDR)的邮箱地址。它与收件人可见的“From”地址分开,仅用于接收自动投递通知。
退信地址如何工作
邮件传输会使用两组地址:
标头 From(收件人可见):From: John Doe <[email protected]>
信封 From(SMTP 层使用,用于退信):
MAIL FROM: <[email protected]>
投递失败时,收件服务器会把退信发送给信封发件人,而不是标头 From 地址。
SMTP 会话示例
Client: MAIL FROM: <[email protected]>
Server: 250 OK
Client: RCPT TO: <[email protected]>
Server: 550 No such user here
Client: QUIT
# Later, server sends bounce to [email protected]
邮件退信的类型
硬退信
永久性投递失败:
- 用户不存在:550 No such user
- 域名不存在:550 Domain not found
- 被策略拒绝:550 Spam blocked
软退信
暂时性失败:
- 邮箱已满:452 Insufficient system storage
- 服务器暂时不可用:421 Service not available
- 邮件过大:552 Message size exceeds limit
封锁退信
与送达性有关的问题:
- IP 被列入黑名单:554 Service unavailable; Sender IP blocked
- 内容被判定为垃圾邮件:550 Spam score too high
- 受到速率限制:450 Too many emails
退信地址配置
在邮件标头中设置 Return-Path
PHP(mail 函数):$to = "[email protected]";
$subject = "Test Email";
$message = "Email body";
$headers = "From: [email protected]\r\n";
$headers .= "Return-Path: [email protected]\r\n";
mail($to, $subject, $message, $headers, "-f [email protected]");
PHPMailer:
$mail = new PHPMailer();
$mail->From = "[email protected]";
$mail->Sender = "[email protected]"; // Return path
$mail->addAddress("[email protected]");
$mail->Subject = "Test Email";
$mail->send();
Postfix(SMTP):
# /etc/postfix/main.cf
sender_canonical_maps = hash:/etc/postfix/sender_canonical
# /etc/postfix/sender_canonical
@example.com [email protected]
# Apply changes
postmap /etc/postfix/sender_canonical
systemctl reload postfix
专用退信处理服务
大多数邮件服务商都提供退信管理:
Amazon SES:{
"Message": {
"Subject": "Test",
"From": "[email protected]",
"ReturnPath": "[email protected]"
}
}
SendGrid:
const msg = {
to: '[email protected]',
from: '[email protected]',
replyTo: '[email protected]',
return_path: '[email protected]',
subject: 'Test Email',
text: 'Email body'
};
退信地址的命名约定
子域名方式
[email protected] # General bounces
[email protected] # No-reply emails
[email protected] # Returns/receipts
按活动区分
按营销活动跟踪退信:
[email protected]
可变信封返回路径(VERP)
把收件人编码进退信地址:
Sending to: [email protected]
Return path: [email protected]
When bounce arrives at bounces+*, parse to identify failed recipient
处理退信邮件
自动解析退信
Python 示例:import email
from email import policy
def parse_bounce(raw_email):
msg = email.message_from_string(raw_email, policy=policy.default)
# Extract bounce type
if "550" in msg.get_payload():
return "hard_bounce"
elif "452" in msg.get_payload():
return "soft_bounce"
# Extract failed recipient
for part in msg.walk():
if part.get_content_type() == "message/delivery-status":
# Parse delivery status
pass
return bounce_info
# Integrate with mailing list to remove hard bounces
基于 Webhook 的退信处理
现代 ESP 提供 Webhook:
SendGrid Webhook:POST /bounce-webhook
{
"email": "[email protected]",
"event": "bounce",
"reason": "550 5.1.1 User unknown",
"type": "blocked",
"status": "5.0.0"
}
处理方式:更新数据库,将邮件标记为已退信。
SPF 与退信地址
SPF 检查信封发件人(退信地址),而不是 From 标头:
Message:
From: [email protected] (header)
Return-Path: [email protected] (envelope)
SPF Check:
Queries: mail-server.com TXT record (not example.com)
Must include sending IP in mail-server.com's SPF
退信域名的 SPF 配置
bounces.example.com. IN TXT "v=spf1 include:_spf.sendgrid.net ~all"
确保退信子域名具有适用于发信基础设施的 SPF 记录。
退信地址最佳实践
使用专用退信地址
不要使用主邮箱接收退信:
# Bad
Return-Path: [email protected]
# Good
Return-Path: [email protected]
监控退信率
| 退信率 | 评估 | 处理方式 |
|---|---|---|
| < 2% | 健康 | 继续监控 |
| 2-5% | 需关注 | 审查邮件列表质量 |
| 5-10% | 较差 | 立即清理列表 |
| > 10% | 严重 | 送达性面临风险 |
实施退信处理
自动移除硬退信:
-- Mark emails with hard bounces
UPDATE mailing_list
SET status = 'bounced', bounce_count = bounce_count + 1
WHERE email IN (SELECT email FROM recent_hard_bounces);
-- Remove after 3 hard bounces
DELETE FROM mailing_list
WHERE bounce_count >= 3;
分开处理事务邮件和营销邮件退信
[email protected] # Order confirmations, receipts
[email protected] # Newsletters, campaigns
两类邮件的退信率预期不同。
设置自动退信处理
Cron 任务示例:#!/bin/bash
# Process bounces every hour
# Fetch bounces from IMAP
fetchmail -c /etc/fetchmailrc
# Parse and update database
/usr/local/bin/process-bounces.py
# Clean up processed bounces older than 30 days
find /var/mail/bounces -mtime +30 -delete
反向散射与退信安全
反向散射问题
服务器接受垃圾邮件后再发送退信,就可能把邮件发给伪造的地址:
1. Spammer sends email with forged From
2. Your server accepts it
3. Your server realizes it's spam/invalid
4. Your server bounces to forged From address
5. Innocent party receives bounce (backscatter)
解决方案:在 SMTP 阶段拒绝邮件,不要先接受再退信:
# Postfix: Reject unknown users at SMTP time
smtpd_recipient_restrictions = reject_unauth_destination
local_recipient_maps = hash:/etc/postfix/local_recipients
伪造退信
攻击者可以伪造退信来:
- 收集有效邮箱地址
- 伪装成退信投递垃圾邮件
- 通过虚假的投递报告窃取凭据
- 检查退信是否来自 MX 服务器
- 确认退信对应的是你实际发送过的邮件
- 仔细解析 delivery-status 标头
常见退信场景
场景 1:所有邮件都退信
原因:SPF 失败、IP 被列入黑名单或服务器信誉不佳 检查:SPF 记录、发件 IP 信誉和 DMARC 报告场景 2:收不到退信
原因:退信地址配置错误或地址不存在 检查:退信域名的 MX 记录和邮箱是否存在场景 3:软退信率很高
原因:收件服务器过载、速率限制或邮件过大 检查:发送速率、邮件大小和收件服务器错误场景 4:退信循环
原因:退信地址触发自动回复,自动回复又触发另一封退信 检查:关闭退信地址上的自动回复测试退信处理
向无效收件人发送测试邮件:# Test bounce to invalid address
swaks --to [email protected] \
--from [email protected] \
--server mx.test-domain.com
# Check if bounce arrives at [email protected]
验证退信域名的 SPF:
dig bounces.example.com TXT
# Should show SPF record with authorized senders
正确管理退信地址对于维护发件人信誉、保持列表卫生和提高邮件送达率至关重要。