多种方法使WordPress发送邮件

1 电子邮件服务原理

1.1 电子邮件系统的组成

最基本的电子邮件系统由四部分组成:发件人用户代理,发送方邮件服务器,接收方邮件服务器,收件人用户代理。

  1. 发件人用户代理:发信人(用户)调用用户代理来撰写和编辑要发送的邮件,用户代理通过SMTP协议把邮件发送给发送方邮件服务器;
  2. 发送方邮件服务器:发送方邮件服务器将邮件放入邮件缓存队列中等待发送。运行在该服务器上的SMTP客户端进程发现邮件缓存中有待发送邮件,就向运行在接收方邮件服务器上的SMTP服务器进程建立TCP连接,然后发送邮件,发送结束后关闭连接;
  3. 接收方邮件服务器:运行在接收方邮件服务器中的SMTP服务进程接收到邮件后,将邮件放入到收件人用户的邮箱中;
  4. 接收人用户代理:收件人打算收信时,调用用户代理,使用POP(或IMAP)协议将邮件从接收方邮件服务器的用户邮箱中取回。

值得注意的是,这里发送方邮件服务器和接收方邮件服务器的概念是相对的。它们实际上同时充当客户端和服务器,即每方服务器同时运行STMP客户端进程和SMTP服务进程。

流程图为:

graph TB;
    A((用户))-->|编辑|B;
    B[发件人用户代理]-->|SMTP|C[发送方邮件服务器];
    C-->|查询目标服务器DNS后 使用STMP发送|D;
    D[接收方邮件服务器]-->|POP/IMAP|E[接收人用户代理];
    E-->|MIME解析|F((用户));

以163邮箱为例,其邮箱相关服务器信息为:

服务器名称 服务器地址 SSL协议端口 非SSL协议端口号
IMAP imap.163.com 993 143
SMTP smtp.163.com 465/994 25
POP3 pop.163.com 995 110

1.2 SMTP/POP3/IMAP/MIME协议原理

1.2.1 SMTP

SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是用于传送电子邮件的机制。该协议是在RFC-821中定义的。采用Client/Server工作模式,默认使用TCP 25端口。

SMTP不使用中间的邮件服务器,TCP总在发送方和接收方这两个邮件服务器之间直接连接.当两方邮件服务器因故障而暂时不能建立连接时,发送方的邮件服务器只能等一段时间后再进行尝试。

sequenceDiagram;
    participant a as 流程;
    participant s as Socket;
    participant p as SMTP;
    Note over s: 等待
    a-->p:客户端使用TCP协议连接SMTP服务器的25端口
    s-->p:Host:post
    Note right of s:getOutputStrream()
getInputStream() a-->p:客户端发送HELO报文将自己的域地址告诉给SMTP服务器 s-->p:send Note right of s:HELO a-->p:SMTP服务器接受连接请求,向客户端发送请求账号密码的报文 s-->p:send Note right of s:AUTH LOGIN a-->p:客户端向SMTP服务器传送账号和密码,验证成功则回复OK p->>s:send Note right of s:Base64:

s->>p:send Note right of s:OK a-->p:客户端使用MAIL命令将邮件发送者的名称发送给SMTP服务器 s-->p:send Note right of s:MAIL FROM<> a-->p:SMTP服务器发送OK命令做出响应 s-->p:send Note right of s:OK a-->p:客户端使用RCPT命令发送邮件接收者地址,SMTP服务器识别后回复OK p->>s:send Note right of s:RCPT TO<> s->>p:send Note right of s:OK a-->p:收到SMTP服务器的OK命令后,客户端使用DATA命令发送邮件的数据 s-->p:send Note right of s:DATA a-->p:客户端发送QUIT命令终止连接 s-->p:send Note right of s:QUIT

1.2.2 POP3

POP3(Post Office Protocol 3,邮局协议版本3)是简单但是功能有限的邮件读取协议,现在使用的是它的第三个版本POP3。POP3使用“PULL(拉)”的通信方式,当用户在读取邮件时,用户代理向邮件服务器发起请求,拉取客户邮箱种的邮件。其工作模式有“下载并保留”与“下载并删除”两种。

POP3协议是在RFC-1939中定义的,也是Internet上的大多数人用来接收邮件的机制。POP3采用Client/Server工作模式,默认使用TCP 110端口。

sequenceDiagram
    participant a as 流程
    participant s as Socket
    participant p as POP3
    Note over s: 等待
    a-->p:客户端使用TCP协议连接邮件服务器的110端口
    s-->p:Host:post
    Note right of s:getOutputStrream()
getInputStream() a-->p:客户端使用USER与PASS命令将邮箱的账号传给POP3服务器 s-->p:send Note right of s:USER xxx
PASS xxx a-->p:客户端使用STAT命令请求服务器返回邮箱的统计资料 s-->p:send Note right of s:STAT a-->p:客户端使用LIST命令列出服务器里邮件数量 s-->p:send Note right of s:LIST... a-->p:客户端使用RETR命令接收邮件,并用DELE命令标记邮件服务器中的邮件 s-->p:send Note right of s:RETR... a-->p:客户端发送QUIT命令,邮件服务器将将置为删除标志的邮件删除 s-->p:send Note right of s:QUIT a-->p:连结结束,断开 Note right of s:close()

1.2.3 IMAP

IMAP(Internet Mail Access Protocol,交互式邮件存储协议)它是跟POP3类似邮件访问标准协议之一。不同的是,IMAP4改进了POP3的不足,用户可以通过浏览信件头来决定是否收取、删除和检索邮件的特定部分,还可以在服务器上创建或更改文件夹或邮箱。开启了IMAP后,用户在电子邮件客户端收取的邮件仍然保留在服务器上,同时在客户端上的操作都会反馈到服务器上,如:删除邮件,标记已读等。服务器上的邮件也会做相应的动作。所以无论从浏览器登录邮箱或者客户端软件登录邮箱,看到的邮件以及状态都是一致的。

IMAP除了支持POP3协议的脱机操作模式外,还支持联机操作和断连接操作。它为用户提供了有选择的从邮件服务器接收邮件的功能、基于服务器的信息处理功能和共享信箱功能。IMAP4的脱机模式不同于POP3,它不会自动删除在邮件服务器上已取出的邮件,其联机模式和断连接模式也是将邮件服务器作为“远程文件服务器”进行访问,更加灵活方便。IMAP4支持多个邮箱。

IMAP4的这些特性非常适合在不同的计算机或终端之间操作邮件的用户(例如你可以在手机、PAD、PC上的邮件代理程序操作同一个邮箱),以及那些同时使用多个邮箱的用户。它可以用来从本地邮件客户端(如Microsoft Outlook、Outlook Express、Foxmail、Mozilla Thunderbird)访问远程服务器上的邮件。其默认端口号为143。

1.2.4 MIME

MIME(MIME,Multipurpose Internet Mail Extensions,多用途互联网邮件扩展)是一个互联网标准,它是对SMTP的拓展,使其能够支持:

  1. 非ASCII字符文本;
  2. 非文本格式附件(二进制、声音、图像等);
  3. 由多部分(multiple parts)组成的消息体;
  4. 包含非ASCII字符的头信息(Header information)。这个标准被定义在 RFC 2045、RFC 2046、RFC 2047、RFC 2048、RFC 2049 等RFC中。

2 解决wordpress无法发送邮件问题

错误提示:无法发送电子邮件,可能原因:您的主机禁用了mail()函数。

默认情况下,WordPress使用PHP mail()函数发送由WordPress或任何联系人插件(如WPForms)生成的电子邮件。丢失这些电子邮件的最常见原因是服务器未配置为使用PHP mail()函数。可以使用以下方法检测主机能不能发送STMP邮件:

新建一个php文件,命名为 mail.php ,打开并填上以下代码:

1
2
3
4
5
6
7
8
<?php
$txt = "hello";
// 以下的邮箱地址改成你的
$mail = 'cakipual@gmail.com';
// 发送邮件
mail($mail, "My subject", $txt);
echo 'message was sent!';
?>

保存并上传至你的网站根目录,浏览器地址栏键入: http://主页地址/mail.php
之后查收邮件,如果收到标题为 My subject 的邮件,说明主机支持mail()函数发送邮件,也就是WordPress默认的发信方式,可以通过以下方式设置wordpress:WordPress无法发送邮件的解决方法

执行以上操作后您网站的邮件收发功能应该就可以实现了。另外也可以使用代理:

3 使用代理

3.1 GMail API

Google API官网
拥有一个Google邮箱之后,我们就可以使用谷歌提供的大部分免费服务了。进入谷歌API控制台,新建项目,需要提供以下信息:

  • 您使用的是哪种API? Gmail API
  • 你将从哪里调用API? Web服务器(例如node.js,Tomcat)
  • 您将访问哪些数据? 用户数据

下一步将创建OAuth(Open Authorization)客户端ID。OAuth将为网站提供使用Gmail帐户验证电子邮件的权限。

对于“名称”字段,输入任何自定义名称,这仅在Google帐户中使用;
对于授权的JavaScript源,输入站点的URL;
最后填写授权重定向URI字段。要获取网站的URI,需要使用WordPress网站返回标签页或窗口。此字段中的值应为网站的网址。

更多参考:

3.2 MailGun

MailGun官网

Mailgun 是一个简便高效的邮件发送云服务,提供丰富的API接口。另外它提供每个月几千乃至一万封的免费邮件发送服务,并有非常高的邮件到达率。其工作原理可以描述为(以MailGun+Gmail为例):

graph LR;
A(Internet)-->|receive|B(MailGun 代理 cakipaul.com);
B-->|resend|C(Gmail);
C-->|SMTP|B;
B-->|sned|A;

在MailGun注册账户后,域名栏下会有一个sandbox服务器。但是最好还是得新建一个自己的Domain,按照提示将自己的域名解析到它给的链接上,Mailgun會告訴你在DNS要設置的解析項目,有2個MX Record、2個TXT Record、1個CName Record。

将自己域名下的邮件服务代理到Mailgun之后,再到Route区选择收信邮箱。透過Mailgun將所有寄到你專門信箱的信,都轉到你慣用的Gmail信箱。

之后到Gmail账户设置,使Gmail也可以用@cakipaul.com的域名发送邮件。这需要在settings(or Manage Labels) - Account and Import - Send mail as里面添加。具体设置方法可参考:Mailgun+Gmail一次搞定WP網站Email相關的所有設置

最后我们就可以通过wordpress提供的插件来设置系统邮件发送方式了。推荐使用WP Mail SMTP或Post SMTP,两者均支持多种代理方式的设置。

更多参考文章:
Php调用MailGun发送邮件方法总结

奇技淫巧——整合Gmail与Mailgun实现免费域名邮箱

3.3 SendGrid

SendGrid官网
使用WP Mail SMTP插件与SendGrid可以便捷实现邮件发送服务。在SendGrid官网注册免费帐户,然后 申请APIKeys 就可以配合插件实现邮件服务了。创建APIKey时可以选择授权范围,一般来说只需要授予发送权限即可。APIKey获得后只会明文显示一次,要记得复制下并来保存在安全的地方。

注意有时QQ邮箱等邮箱服务器会将从SendGrid收到的邮件标记为垃圾邮件,这点目前无能为力:(

我目前使用的免费套餐包括:

Trial 40K
40,000 emails for 30 days
2,000 contacts in Marketing Campaigns
After your trial ends, you can send 100 emails/day free

若想了解更多可参考:How to Setup Reliable WordPress Email Notifications with SendGrid with WP Mail SMTP

4 常见问题

4.1 登陆后菜单栏出现乱码

其格式为: Notice: Undefined offset: 2 in [wordpress目录]/wp-admin/includes/[?].php on line [?]

解决方式:

  1. 抑制其显示:更改php.ini文件中error_repoting的参数为”E_ALL & E_NOTICE”(默认值一般为”E_ALL & ~E_DEPRECATED & ~E_STRICT”),即:error_reporting(E_ALL & E_NOTICE)

    offset:接下去的数字是出错的数组下标,一般是超出了数组的取值范围,如定义了数组$\A[]有10个元数,如果出现了$A[10]就会出现错误(Notice: Undefined offset: 10 ….)

    php.ini文件位置:如果采用RPM包安装,一般位于 /etc/php.ini ;如果采用源代码安装,一般默认安装在/usr/local/lib/php.ini 或 /usr/local/php/etc/php.ini

  2. 修改WordPress的Debug功能
    参考官方
    WP_DEBUG是一个PHP常量(永久全局变量),可用于在整个WordPress中触发“调试”模式。默认情况下,它假定为false,并且通常在WordPress的开发副本上的 wp-config.php文件中设置为true。

    1
    define('WP_DEBUG'false);

配置为false后错误提示消失。
ps.建议不要在实时站点上使用WP_DEBUG或其他调试工具; 它们用于本地测试和暂存安装。
启用WP_DEBUG将导致显示所有PHP错误,通知和警告。这可能会修改PHP的默认行为,该行为仅显示致命错误和/或在达到错误时显示白屏死机。
显示所有PHP通知和警告通常会导致错误消息,这些消息似乎没有被破坏,但不遵循PHP中正确的数据验证约定。一旦识别出相关代码,这些警告就很容易修复,并且生成的代码几乎总是更容易出错并且更容易维护。
启用WP_DEBUG还会导致在您的站点上使用的WordPress中已弃用的函数和参数的通知。这些函数或函数参数尚未从核心代码中删除,但将在不久的将来删除。弃用通知通常表示应该使用的新功能。

  1. 其他方法
    参考:解决出问题的插件

4.2 wordpress修改固定链接格式后出现404页面

这需要网站环境支持伪静态,可以通过写改配置文件来实现。以本站Nginx服务器为例:
打开nginx.conf,在server{} 里添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
location / {  
index index.html index.php;
if (-f $request_filename/index.html){
rewrite (.*) $1/index.html break;
}
if (-f $request_filename/index.php){
rewrite (.*) $1/index.php;
}
if (!-f $request_filename){
rewrite (.*) /index.php;
}
}
rewrite /wp-admin$ $scheme://$host$uri/ permanent;

保存以后,执行 service nginx restart 即可。

更多设置参考:WordPress更改固定链接404的解决办法