通过 PHP 向许多用户发送邮件的安全方法

发布于 2024-09-25 20:00:59 字数 518 浏览 2 评论 0原文

让我解释一下标题中的意思。假设我正在为一个网上商店/目录创建一个小型电子商务系统。客户可以选择是否希望收到新闻通讯。如果他们这样做,那么从逻辑上讲,应该在新闻通讯形成并准备就绪后立即发送新闻通讯。

当然,可以简单地通过从数据库中获取所有指定的用户电子邮件并使用 for 循环通过循环中的 mail 函数发送邮件来完成,但问题是我是告诉,这是不好的做法。简单且不便宜的方法是购买互联网服务来发送新闻通讯,但是对于 php 程序员来说需要什么呢?

所以请问各位战友,从你们的角度来看,可能有什么解决办法呢?

注意!您可能不会相信我,但这不是为了发送垃圾邮件。

UPD:我可能对自己的解释是错误的,但我想听到一个解决方案,不仅关于发送邮件的正确方式,而且关于正确的投递。因为并非所有发送的邮件都会被送达。
当然,还有一些不可预测的原因。例如,在途中的某个地方,有些东西坏了,邮件丢失了(如果可能的话),但也有其他原因可能受到服务器或其他地方的影响。也许需要和房东谈谈?

Let me explain what I mean in my title. Let's say, that for example I'm creating a small e-commerce system for one web shop/catalog. There's a possibility for customers to choose, do they wish to receive newsletters or not. If they do, then logically thinking the newsletters should be send immediately as the newsletter is formed and ready.

Of course it can be done simple by fetching all specified user e-mails from database and using for cycle to send mail via mail function in loop, but problem is that I was told, that this is bad practice. The simple and not cheap way would be purchasing internet service for sending newsletters, but what for the php programmer is needed, then?

So I ask you humble comrades, what from your point of view might be a solution?

NB! You probably won't believe me, but it is not for spamming.

UPD: I might have explained myself wrong, but I would like to hear a solution not only about the correct way to send mail, but also about the correct delivery. Since not every mail send is always delivered.
Of course there are some reasons, which are unpredictable. For example someplace along the way something broke and mail was lost (if such thing is possible), but there are also other reasons which are influenced maybe from server or elsewhere. Maybe there is need to talk with hoster about it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

多孤肩上扛 2024-10-02 20:00:59

尽管我不会将其作为网络请求/HTTP 流程的一部分,但您没有理由不能用 PHP 编写它。我已经成功实现了每封邮件可容纳 500,000 个订阅者(取决于可用的本地数据,因为这是一个特定于位置的项目)。这是一个内部项目,因此不幸的是没有适合您的代码/包,但我遇到了一些提示:

设置交付

  • 从 phpmailer 本身开始,负责内容的格式化、编码和标题、添加附件等。这部分效果很好,我不想从头开始编写。
  • 电子邮件本身的“发送”只是在数据库中设置一些标志是否/如何/什么应该发送给(一部分)订阅者。
  • 设置此标志后,它将自动被 cronjob 拾取,不再涉及网络服务器。
  • 我从一个污染严重的数据库开始,其中包含数百万个电子邮件地址,其中很多明显无效,因此首先要验证所有电子邮件地址的格式,然后验证主机:
    • filter_var($email, FILTER_VALIDATE_EMAIL); 对订阅者(并明显存储结果)删除了前几十万封无效电子邮件。
    • 从电子邮件中分离出主机(并存储主机名),并验证它(它在 DNS 中是否有 MX 或至少有 A 记录,但请记住:您可以发送电子邮件到 IP 地址 foo@[255.255.255.255],因此请保持这些有效))又摆脱了很大一部分。此处的电子邮件地址并未永久禁用,但带有一个状态标志,表明它们因域名/IP 原因而被禁用。
    • 脚本已更改为需要在订阅时/插入之前提供有效的电子邮件地址,这种胡言乱语的“你不会在任何地方得到它@”数据库中的订阅污染只是可笑。
  • 现在我得到了一个可能有效的电子邮件地址列表。本质上有 3 种方法可以检测无效地址(请记住,所有都可以是临时的):
    • 服务器会立即拒绝它们。
    • 之前确定的服务器只是不监听流量。
    • 在您认为已送达很久之后,它们就被退回了。
  • 奇怪的是,退回邮件似乎每个电子邮件服务器都有另一种格式,一开始解析起来非常困难,但最终实际上很容易使用 VERP。不是解析整个电子邮件,而是一个专用的电子邮件地址(我们称之为 [email protected])被配置为而不是传递到邮箱,通过命令进行管道传输,如果我们将电子邮件发送到 [电子邮件受保护]Return-Path 已设置为 [电子邮件受保护]。在收据上轻松解析,在多少次退回邮件(邮箱可能不存在、邮箱可能已满(是的,仍然是!)等)之后,您可以声明电子邮件地址不可用。
  • 现在,直接被服务器拒绝了。也许我们可以正确配置一些 MTA 和/或为它们编写插件,但由于电子邮件对时间敏感,我们必须对每个邮件在最后可用的交付时间(之后电子邮件不再更长)进行绝对可配置的控制用户感兴趣的),每个接收服务器的限制,以及一般所有事情,大约需要在 PHP 中编写一个我们更了解的邮件程序的时间,该邮件程序直接使用 SMTP 协议连接到接收服务器上的套接字 25。只需付出最少的努力,就可以内置另一种传输方式,然后是 PHPMailer 中的默认选择。 SMTP 协议实际上非常简单,但有一些注意事项:
    • 许多接收服务器都采用灰名单:大多数垃圾邮件机器人不会真正关心特定邮件是否到达,他们只是将其大量删除。因此,如果未知/尚不可信的发件人发送邮件,它将被暂时拒绝。捕获该消息(通常是代码 451),并将电子邮件放入队列中以供稍后重试。
    • 邮件服务器,尤其是较大的 ISP 和免费服务(gmail、hotmail/msn/live 等)不会忍受大量邮件而不反击:在前几百/千之后,他们开始拒绝你。稍后会详细介绍。

提高速度

  • 现在,我们有了一个可以运行的交付系统,但它需要。如果您只有 10,000 个地址要发送,那么一小时内发送 10,000 封电子邮件就没有问题,但我们要求的最低发送量约为每小时 200,000 封。首先是一个专用服务器(实际上它的功率可能非常低,无论您做什么,发送电子邮件所花费的大部分时间都在网络中,而不是在您的服务器上)。
  • IP 缓存:还记得我们从电子邮件地址中的主机名请求的所有 IP 吗?我们显然存储了这些,并且一次又一次地查找它们的 IP 会导致相当大的延迟。然而,IP 可能会发生变化:一个 DNS 记录在另一个地方,另一个 MX 在另一个地方......数据很快就会过时。大多数时候,服务器实际上并没有发送任何内容(显然,订阅时事通讯会突然出现),低优先级的 cronjob 正在运行,检查所有具有过时 IP 的主机名(我们选择早于 1 天的主机名作为过时的 IP 地址) ,包括那些以前没有的(新域名一直在注册,那么为什么在有人已​​经热情地使用他/她的全新电子邮件地址订阅后的第二天,域名就不能变得可用?或者服务器某些域的问题得到解决,等等)。实际上,现在发送电子邮件不再需要进行域查找。
  • 重用 SMTP 连接:当您直接与端口 25 通信时,设置与服务器的连接需要相对大部分时间来发送电子邮件。您不必为每封电子邮件设置新连接,您可以可以通过同一连接发送下一个。一些尝试和错误导致此处将默认值设置为每个连接大约 50 封电子邮件(假设您有那么多或更多的域)。但是,在电子邮件地址失败时,关闭并重新打开连接进行重试有时会有所帮助。总而言之,这确实有助于加快事情的进展。
  • 一些显而易见的问题,太明显了,我差点忘了提一下:当场创建电子邮件正文是一种浪费:如果是普通邮件,请准备好正文(我对 PHPMailer 进行了一些修改,以便能够使用缓存的电子邮件),可能是几天前(如果您知道您将在周五发送邮件,并且您的服务器处于空闲状态,为什么不在周三就准备好邮件呢?如果是个性化的,您如果没有足够的时间,仍然可以提前准备好,至少可以让非个性化部分等待
  • 多个过程。我是否提到过,发送电子邮件所需的大部分时间都花在网络上?几乎可以充分利用您的电子邮件服务器,几乎没有明显的负载,并且邮件正在慢慢流出,尝试使用多个邮件发送队列的不同部分来查看什么适合您的服务器/连接,但请记住两件非常重要的事情:
    • 不同的进程使您非常容易受到竞争条件的影响:绝对确定您拥有一个完全可靠的系统,永远不会将同一封邮件发送两次(三次,甚至更多) )。它不仅会严重惹恼用户,而且您的垃圾邮件评级还会上升一个档次。
    • 尽可能将域名放在一起:从队列中随机选择,您将失去与接收该域名的电子邮件的服务器保持开放连接的优势。

避免拒绝

  • 您将发送大量邮件。这正是垃圾邮件发送者所做的。但是,您不想被视为垃圾邮件发送者(毕竟您不是,不是吗)?有多种机制可以彻底提高您对接收服务器的信任度:
  • 拥有适当的反向 DNS:进程会检查属于发送电子邮件的 IP 的 DNS,就像非常,如果二级域名匹配:您是否代表 example.com 发送邮件?确保您的服务器的反向 DNS 类似于 somename.example.com
  • 为您的域发布 SPF 记录:明确表明允许使用用于发送批量电子邮件的计算机预计发送带有 From / Return-Path 标头的邮件。
  • 记住拒绝:服务器不喜欢它一次又一次地告诉您不同的电子邮件地址不存在。当我们处理所有(不再)存在的未经验证的电子邮件地址时,自动化机制,甚至人类管理员都阻止了我们的服务器。直到后来我们才采用双重选择加入,因此数据库被打字错误、人们交换 IP 以及电子邮件地址、恶作剧电子邮件地址等所污染。请务必捕获这些无效者,并在出现足够多或严重的失败时,取消订阅它们。他们对你没有好处,他们占用资源,如果他们真的想要你的邮件并且邮箱稍后可用,他们只需要重新订阅。
  • DKIM 是另一种可以提高您的可信度的机制,但由于我们尚未实施它(尚未),因此我无法告诉您太多相关内容。
  • MX 记录:如果您的发送服务器也是域的接收服务器,某些服务器仍然喜欢它。当时,我们只有 1 个 MX,而且邮件服务器还不是很忙,我们将其称为域的后备 MX 服务器。普通的 MX 服务器不是发送订阅的服务器,因为由于您已经发送了重要电子邮件而被您尝试向其(客户端等)发送重要电子邮件的服务器暂时阻止,这是非常令人恼火的一堆不太重要的邮件。它确实具有接收 MX 的最高优先级,但如果它失败,我们有一个不错的好处,即我们的订阅发送服务器仍将作为后备交付,因此在危机中我们仍然可以到达它,防止客户尝试时尴尬的反弹联系我们。
  • 告诉他们关于你的事。严重地。像 live.com 这样的免费电子邮件地址中的许多主要参与者都为您提供了以某种方式注册的机会,或者有一些联系点可以寻求帮助和帮助。如果您的电子邮件被拒绝,请提供支持。如果您有正当理由发送这么多电子邮件,并且您有这么多订阅者,那么他们很可能会严重增加您每小时可以发送到他们的服务器的电子邮件数量。如果你有足够的说服力和诚实的话,区区 1,000 个人可能会变成一万甚至更多。可能有您必须履行的合同、要求以及您必须做出(并遵守)的承诺才能允许这样做。 ISP 的品牌各有不同,其他玩家也各不相同。通常不必费心打电话给他们,因为 99% 的情况下,你能找到的唯一号码只会有人愿意对你的互联网连接进行故障排除,而他们了解(或被允许)很少了解其他内容。 abuse@ 电子邮件地址是一个很好的起点,但看看您是否可以从某处挖掘出更切题的电子邮件地址。准确、诚实和完整:大约有多少订阅者拥有该 ISP 的电子邮件地址,您尝试向他们发送邮件的频率如何,您收到的错误或拒绝是什么,订阅和订阅情况如何。 unscubscribe 流程是什么样的,以及您实际向他们的客户提供的服务是什么。另外,要友善:发送这些邮件对您的业务可能非常重要,对此感到恐慌并声称遭受严重损失与他们无关。礼貌地陈述事实和愿望,并询问他们是否可以提供帮助,而不是要求解决方案,这会大有帮助。
  • 限制:无论您尝试多少,某些服务器每小时和/或每天仅接受一定数量的邮件。了解这些数字(无论如何我们都会记录成功和失败),将它们设置为正常域的合理默认值,将它们设置为较大玩家商定的限制。

避免被标记为垃圾邮件

  • 第一条规则:不要发送垃圾邮件!
  • 第二条规则:永远!不是“一次性”,不是“他们还没有订阅,但这对他们来说可能是一生的交易”,不是出于最好的意图,人们不得不索要你的电子邮件。
  • 显然建立了正确的双重选择加入订阅机制。
  • PHPMailer 确实自行设置了适当的标头,
  • 通过网络设置了一个简单的取消订阅机制(在每封邮件中包含指向它的链接),也可能通过电子邮件和客户服务(如果有的话)。确保客户服务可以直接取消订阅。
  • 如前所述:取消订阅(过多)失败&弹跳。
  • 避免垃圾邮件“一生的交易”措辞。
  • 谨慎在电子邮件中使用网址。
  • 避免添加指向您无法控制的域的链接,除非您完全确定您可以相信它们不会发送垃圾邮件,即使这样...
  • 为用户提供价值:通过以下方式中的用户交互被标记为垃圾邮件: google/yahoo/live 网络邮件客户端严重损害了未来的成功(在网站上注意:如果您注册了它,live/msn/hotmail 会将您的域发送的所有邮件转发给您,这些邮件被用户标记为垃圾邮件。学会爱它,一如既往:取消订阅他们,他们显然不想要您的购物中心,并且正在损害您的垃圾邮件评级)。
  • 监控您的 IP 黑名单。如果您出现在其中之一,那就再见了,因此需要立即采取行动,洗清自己的罪名并确定案件。

测量成功率

  • 当整个过程都在您的控制之下时,您有理由确定电子邮件最终到达了某个地方(尽管它可能是 MX 的位桶或垃圾邮件文件夹),或者您已记录失败&原因。这涉及“实际交付”的数字。
  • 有些人会试图说服您在电子邮件中添加在线图像的链接(无论是真实的还是著名的 1x1 透明 gif),以衡量有多少人真正阅读了您的电子邮件。由于遮挡这些图像的比例很高,因此这些数字充其量是不稳定的,我们认为我们不应该打扰它们,它们的数字完全不可靠。
  • 如果您希望用户做某事,那么衡量实际成功率的最佳选择会容易得多。向邮件中的链接添加参数,以便您可以衡量有多少用户到达您链接的网站,他们是否执行了所需的操作(观看视频、发表评论、购买商品)。

总而言之,包括所有日志记录、用户界面、每个域/电子邮件/用户的可配置设置等。我们花了大约 1.5 个人月的时间来构建和构建它。消除怪癖。与外包电子邮件相比,这可能是一项相当大的投资,也可能不是,这完全取决于数量和电子邮件的数量。业务本身。

现在,让我们开始吧,我是一个傻瓜,用 PHP 编写 MTA,我非常喜欢它(这是我编写如此大量文本的原因之一),以及极其通用的日志记录和日志记录功能。设置功能、基于故障百分比的每台主机警报等让直播变得如此简单;)

There is no reason why you couldn't write it in PHP, although I would not make it a part of a webrequest / HTTP process. I've successfully implemented for give or take 500,000 subscribers per mailing (depending on local data available, as this was a location-specific project). It was an in-house project, so unfortunately no code/package for you, but some pointers I came across:

Setting up delivery

  • Started out with phpmailer itself, to take care of formatting, encoding of contents and headers, adding of attachments etc. That portion of it works well, and I wouldn't want to write that from scratch.
  • The 'sending' of an email itself is just setting some flag in a database whether / how / what should be sent to (a portion of) the subscribers.
  • After this flag is set, it will automatically be picked up by a cronjob, no more webserver involved.
  • I started out with a heavily polluted database with millions of email addresses, of which a lot were obvious not valid, so first thing was to validate all email addresses for format, then for host:
    • filter_var($email, FILTER_VALIDATE_EMAIL); over the subscribers (and storing the result obviously) got rid of the first few hundred thousand invalid emails.
    • Splitting out the host (and storing the host name) from the emails, and validating that (does it have an MX or at least an A record in DNS, but keep in mind: you can send email to an IP-address foo@[255.255.255.255], so do keep those valid)) got rid of a good portion more. The emailaddresses here are not permanently disabled, but with a status flag that indicates they're disabled because of the domain name / ip.
    • Scripts were changed to require valid emailaddresses on subscription / before insertion, this nonsense of 'you aint gonna get it@anywhere' subscription-pollution in the database was just ridiculous.
  • Now I ended up with a list of email addresses that had the potential of being valid. There are in essence 3 ways to detect invalid addresses (keep in mind, the all can be temporary):
    • They are denied immediately by the server.
    • The earlier determined server just doesn't listen to traffic.
    • They are bounced long after you thought you delivered them.
  • Strange thing, the bounces, which every emailserver seems to have another format forand were a hell to parse at first, ended up actually pretty easy to capture using VERP. Rather then parsing whole emails, a dedicated email address (let's call it [email protected]) was configured to rather then deliver to mailbox, to pipe it through a command, and if we sent email to [email protected], the Return-Path was set for [email protected]. Easily parsed on receipt, and after how many bounces (mailbox could not exist, mailbox may be full (yes, still!), etc.) you declare an emailaddress unusable is up to you.
  • Now, the direct denial by the server. Probably we could have gone with properly configuring some MTA and/or writing plugins for those, but as the emails were time-sensitive, and we had to have absolute configurable control per mailing over last usable delivery time (after which the email was not longer of interest to the user), throttling per receiving server, and generally everything, it would take about the same time writing a mailer in PHP which we knew better, that used the SMTP protocol directly to socket 25 on receiving servers. With a minimum amount of effort the possibility of another transport then the default choices in PHPMailer was built-in. The SMTP protocol is actually quite simple, but there are some caveats:
    • A lot of receiving server apply Grey Listing: most spambots will not really care if a specific mail arrives, they just churn them out. So, if an unknown / not yet trusted sender send mail, it will be temporarily rejected. Catch that (usually code 451), and place the email in the queue for later retry.
    • A mailserver, especially of the larger ISP's and free services (gmail, hotmail/msn/live, etc.) will not stand for a torrent of mail without fighting back: after the first couple of hundred / thousand, they start rejecting you. More about that later.

Getting speed

  • Now, we had a delivery system that worked, but it needed to be fast. Sending a 10,000 emails in an hour is all fine if you only have 10,000 addresses to send to, but the minimum we required was about 200,000 per hour. Start of it was a dedicated server (which can actually be pretty low powered, no matter what you do, most of time taken in delivery of email is in the network, not on your server).
  • Caching of IP's: remember all those IP's we requested from hostnames in email addresses? We stored those obviously, and looking up their IP's again and again causes considerable lag. However, IPs may change: a DNS record there, another MX at another place... the data gets stale fast. Most of the time the server isn't actually sending anything (subscription newslettters come in bursts obviously), a low-priority cronjob is running checking all hostnames with a stale IP (we chose older then 1 day as being stale) for an IP address, including those which previously had none (new domains get registered all the time, so why shouldn't a domain become available the day after someone already enthusiastically subscribed with his/her brand new email address? Or server problems with some domain are solved, etc.). Actually sending the emails now required no more domain lookups.
  • Reuse of the SMTP connection: setting up a connection to a server takes relatively a large portion of the time to deliver an email when you're talking directly to port 25. You don't have to setup a new connection for every email, you can just send the next over the same connection. A bit of trail-and-error has resulted in setting the default here to about 50 emails per connection (assuming you have that many or more for the domain). However, on failure of an emailaddress closing and reopening the connection for a retry sometimes helped. All in all, this really helped to speed things along.
  • Some obvious one, so obvious I almost forgot to mention it: it would be a waste to have to create the body of the email on the spot: if it's a general mail, have the body ready (I altered PHPMailer somewhat to be able to use a cached email), possibly days before (if you know you're going to send a mail on Friday, and your server is idling, why not prepare them on Wednesday already? If it's personalized, you could still prepare it beforehand given enough time, if not, at least have the non-personalized portions waiting to go.
  • Multiple processes. Did I mention much of the time it takes to deliver email is spend on the network? One mailing process is not nearly getting the most from your emailserver, barely noticable load and the mails are trickling out. Play around with a number of processes mailing different portions of the queue to see what's right for your server/connection, but remember 2 very important things:
    • Different processes make you very vulnerable to race conditions: be freaking absolutely sure you have a fullproof system that will never send the same mail twice (thrice, of even more). Not only does it seriously annoy users, your spamrating goes up a notch.
    • Keep domains together where possible: randomly picking from the queue you will lose the advantage of keeping an open connection to the server receiving email for the domain.

Avoiding rejects

  • You're going to send a lot of mail. That's exactly what spammers do. However, you don't want to be seen as a spammer (after all, you aren't, are you)? There are a number of mechanisms in place which will thoroughly increase your trustworthiness to receiving servers:
  • Have a proper reverse DNS: processes checking the DNS belonging to the IP that is sending the email like it very much if the second level domains match: are you sending mail on behalf of example.com? Make sure your server's reverse DNS is something like somename.example.com.
  • Publish SPF records for your domain: explicitly indicate the machine used to send your bulk email is allowed & expected to send mail with that From / Return-Path headers.
  • remember rejects: servers don't like it to tell you again and again that different email addresses don't exist. Either automated mechanisms, and even human admins, blocked our server while we worked through all non-validated email addresses that did (no longer) exist. We didn't employ a double opt-in until later, so the database was polluted with typos, people switching IPs and thereby email address, prank email addresses and so on. Be sure to capture those invalids, and given enough or sever enough failures, unsubscribe them. They're doing you no good, they're hogging resources, and if they really want you mail and the mailbox becomes available later, they'll just have to resubscribe.
  • DKIM is another mechanism that may increase your trustworthiness, but as we haven't implemented it (yet), I cannot tell you much about that.
  • MX records: some servers still like it if your sending server is also the receiving server for the domain. As it was at the time, we had only 1 MX, and as the mailing server was still not that very busy, we dubbed it the fallback MX server for the domain. The normal MX server was not the server sending the subscriptions, as it is very irritating to be temporarily blocked by a server you're trying to send an important email to (clients etc.) because you already sent a load of less important mail. It does have the highest preference as receiving MX, but in the event it would fail we had the nice bonus that our subscription sending server would still be fallback for delivery, so in crisis we could still get to it, preventing awkward bounces to customers trying to reach us.
  • Tell them about you. Seriously. A lot of major players in free email addresses like live.com offer you the opportunity to sign up in some way, or have some point of contact to go to for help & support if your emails get rejected. I you have a legitimate reason to send so many emails, and it is believable you have that many subscribers, chances are they seriously up the number of emails you can send to their server per hour. A meager 1,000 may become somewhere in the ten-thousands or even higher if you are persuasive and honest enough. There may be contracts, requirements you have to fulfill, and promises you must make (and keep) to be allowed this. ISP's are a brand apart, and every other player is different. Don't bother calling them usually, because 99% of the time the only numbers you can find will only have people willing to troubleshoot your internet connection, which understand (or are allowed) little else. An abuse@ email address is a good place to start, but see if you can delve up a more to-the-point email address up from somewhere. Be precise, honest and complete: roughly how many subscribers of you have an emailaddress with that ISP, how often are you trying to mail them, what are the errors or denials you receive, how is the subscribe & unscubscribe process like, and what is the service you actually provide to their customers. Also, be nice: how vital sending those mails may be to your business, panicking about it and claiming terrible losses does not concern them. A polite statement of facts and wishes, and asking whether they can help rather then demanding a solution goes a very long way.
  • Throttling: as much as you tried, some server will accept only a certain amount of mail per hour and/or day from you. Learn those numbers (we're logging successes & failures anyway), set them to a reasonable default for the normal domains, set them to agreed upon limits for bigger players.

Avoiding being tagged as spam

  • First rule: don't spam!
  • Second rule: ever! Not a 'once off', not a 'they haven't subscribed but this may be the deal of a lifetime to them', not with the best intentions, people have had to ask for your emails.
  • Obviously set up a correct double opt-in subscription mechanism.
  • PHPMailer does set proper headers on its own,
  • Set up an easy unsubscribe mechanism, by web (include a link to it in every mail), possibly also email and customerservice if you have it. Make sure the customerservice can unsubscribe people directly.
  • As said earlier: unsubscribe (excessive) fails & bounces.
  • Avoid spammy 'deal of a lifetime' wordings.
  • Use url's in your emails sparingly.
  • Avoid adding links to domains outside your control, unless you are absolutely sure you can trust them not to spam, if even then...
  • Provide value to the user: being tagged as spam by user-interaction in google/yahoo/live webmail clients seriously hurts future successes (on a site note: if you sign up for it, live/msn/hotmail will forward all mail to you send by your domain which is tagged as spam by users. Learn to love it, and as always: unsubscribe them, they clearly don't want your mall and are hurting your spam rating).
  • Monitor blacklists for your IP. If you appear on one of those, it's bye bye, so immidiate action in both clearing your name and determining the case is required.

Measuring success rate

  • With the whole process under your control, you are reasonably sure the email ended up somewhere (although it could be the MX's bitbucket or a spam folder), or you have logged a failure & the reason why. That takes care of the 'actually delivered' numbers.
  • Some people will try to convince you to add links to online images to your emails (either real or the famous 1x1 transparent gif) to measure how many people actually read your email. As a high percentage blocks those images, these numbers are shaky at best, and our believe is we just shouldn't bother with them, their numbers are utterly unreliable.
  • Your best bet for measuring actual success rate are a lot easier if you want the users to do something. Add parameters to links in the mail, so you can measure how many users arrive at the site you linked, whether they performed desired actions (watched a video, left a comment, purchased goods).

All in all, with all the logging, the user-interface, configurable settings per domain / email / user etc. It took us about 1,5 man-month to build & iron out the quirks. That may be quite an investment compared to outsourcing the emails, it may be not, it all depends on the volume & business itself.

Now, let the flaming begin that I was a fool to write an MTA in PHP, I for one thoroughly enjoyed it (which is one reason I wrote this huge amount of text), and the extremely versatile logging & settings capabilities, per-host alerts based on failure percentage etc. are making live oh so easy ;)

成熟的代价 2024-10-02 20:00:59

使用类似 SwiftmailerPHPmailerZend_mail 是更好的替代品使用简单的 mail() 函数,因为它很容易被标记为垃圾邮件。邮件发送有太多问题需要考虑 - 其中大多数问题都是通过使用预先存在的库来解决的。

手动发送群发电子邮件时需要解决一些问题:

  • 使用不正确的标头。

  • 处理退回邮件

  • 由于电子邮件大量涌入而导致脚本超时。

编辑:

可能不是您正在寻找的答案。但是,我强烈建议您投资诸如 Campaign Monitor邮件黑猩猩。由于此过程不是出于教育目的,而是出于商业目的,因此我强烈建议使用上述服务。

Using something like Swiftmailer, PHPmailer or Zend_mail are much better alternatives to using the simple mail() function as it can be easily marked as spam. There are simply too many issues with mailing that need to be considered - most of these are solved by using pre-existing libraries.

Just a few problems that need to be addressed when sending mass emails manually:

  • Using incorrect headers.

  • Processing bounced messages

  • Timing out of script due to an influx of emails.

Edit:

Probably not the answer you're looking for. But, I would strongly suggest you invest in something like Campaign Monitor or Mail Chimp. Since this process is not for educational purposes, but commercial, I would strongly suggest the above services.

一抹微笑 2024-10-02 20:00:59

我收到了你的问题,但在回答之前,让我先谈谈通常的考虑。首先,我强烈建议使用 Mail Chimp 这样的服务。对于小型工作来说,它是免费的,并且有许多很酷的功能,比如跟踪有多少封电子邮件被打开、有多少被点击、有多少封发送失败……想想帮自己一个忙,不要重新发明轮子。

现在,为了了解目的,让我们来回答您的问题。

首先要记住的是确保你的清单是一个好的清单。怎么做呢?好吧,对于一个好的列表,我指的是有效的电子邮件地址列表。只需在您的页面上放置一份时事通讯表单,其中只有一个字段(可能是验证码,但我认为没有必要)。

将所有输入保存到数据库表中,其中“isValid”字段默认设置为 false,以及任何类型的唯一哈希。然后,您发送一封确认电子邮件,其中包含一个用于确认的链接(带有生成的哈希值),单击该链接将使“isValid”标志为真,以及一个取消链接(始终在您的所有电子邮件中发送此取消链接)。

这就是商店和严肃网站所做的。任何强迫您的客户/访客接收的内容都是不良道德行为(即垃圾邮件)。

第二件事,使用良好的托管服务。垃圾邮件发送者通常会使用过于廉价的服务,而主要的电子邮件服务会将来自这些地址的所有内容列入黑名单。

我知道,如果我问错了,你应该问问自己。不,我不,现在技术性的东西来了。

为什么将邮件函数放在 for 循环中是一种不好的做法?简单的。因为函数 mail 每次被调用时都会执行多个操作。 PHP,将打开与邮件服务器的连接,发送要解析的数据,请求发送,注册邮件服务器状态,关闭连接,冒泡状态以完成您调用的邮件功能并清理内存混乱。

从编程的角度来看,这种连接开销是人们所说的不好的做法。使用 SMTP/IMAP 解决方案更好,因为它优化了此过程。

在科技方面有点低迷,我看到你关于交付的问题。好吧,正如我所说,您有一些方法可以确保您的电子邮件列表足够好。但是,如果发生另一个异常,例如客户服务器断电+不间断故障怎么办?

嗯,PHP保持“请求邮件服务器发送,邮件服务器已发送”的状态。如果邮件服务器发送了您的消息,PHP 将返回 true。时期。

如果客户无法接收或拒绝,您应该检查电子邮件标题和电子邮件状态。这些位于电子邮件服务器上。再次强调,这些信息可以通过 SMTP/POP/IMAP 扩展来访问,而不是通过邮件功能来访问。

如果您想进一步了解,请阅读 IMAP 文档,搜索电子邮件类(phpclasses.org、pear 和 pecl 是最好的研究地点)。

额外提示:RFC 很有用,因为您可以更好地了解电子邮件服务器真正相互通信的内容。

额外提示 2:访问您的 gmail 或 ymail,检查您发送/接收的邮件的“完整版本”并阅读其标题。你可以和他们一起学到很多东西。

I got your question, but before replying, let's me go to usual considerations. First, I strongly recommend using a service like Mail Chimp. It's kinda free for small jobs, and has many cool features, like tracking back how many emails were open, how many were clicked, how many failed on deliver... Think about make a favor to yourself and don't reinvent the wheel.

Now, for getting to know purpose, let's go to the reply to your question.

First thing to have in mind is to enforce your list to be a good one. How to do that? Well, for a good list, I mean a valid email addresses list. Simple put a newsletter form on your page, with just one field (maybe a captcha, but I don't think it is necessary).

Save all input to a database table, with a field "isValid" set as false by default, and any kind of unique hash. Then you send a confirmation email, with a link (with the hash generated) for confimation which when clicked will make the "isValid" flag true, and a link for cancelation (ALWAYS send this cancelation link within all your emails).

This is what stores and serious sites do. Anything that force your customers/visitors to receive is a bad moral practice (ie, spam).

Second thing, use a good hosting service. Too cheap services usually are used by spammers, and major email services blacklist everything coming from those addresses.

I know, you should be asking yourself if I get your question wrong. No, I don't, technical stuff comes now.

Why is a bad practice put a mail function inside a for loop? Simple. Because function mail do several operations everytime it is called. PHP, will open a connection to mail server, send data to be parsed, ask for sending, register mail server status, close connection, bubble up status to finish the mail function you called and clean up the memory mess.

This connection overhead is the problem people state as bad practice from programming point of view. Using a SMTP/IMAP solution is better because it optimizes this process.

A little bit down on tech stuff I see your questions about delivery. Well, as I said, you have some ways to ensure you emails list is good enough. But what if another exception occurs, like having a blackout + no-break failures on customer server?

Well, PHP keeps the status of "asking mail server to send, mail server sent". If the mail server sent your message, PHP will return true. Period.

If client was unable to receive, or reject, you should check email headers and email status. These are on email server. Once again, these informations can be accessed with SMTP/POP/IMAP extensions, not with mail function.

If you wanna go further, read IMAP docs, search for email classes (phpclasses.org, pear and pecl are best places to look into).

Extra tip: RFCs can be useful, as you can understand better what email servers really talks to each other.

Extra tip 2: Access you gmail or ymail and check for your sent/received messages for their "full version" and read its headers. You can learn a lot with them.

悟红尘 2024-10-02 20:00:59

只需使用 PHP Mail 并研究 IMF 以及如何构建可以附加第四个的自定义标头参数,exmaple

<?php
// multiple recipients
$to  = '[email protected]' . ', '; // note the comma
$to .= '[email protected]';

// subject
$subject = 'Birthday Reminders for August';

// message
$message = '
<html>
   ...
</html>
';

// To send HTML mail, the Content-type header must be set
$headers  = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";

// Additional headers
$headers .= 'To: Mary <[email protected]>, Kelly <[email protected]>' . "\r\n";
$headers .= 'From: Birthday Reminder <[email protected]>' . "\r\n";
$headers .= 'Cc: [email protected]' . "\r\n";
$headers .= 'Bcc: [email protected]' . "\r\n";

// Mail it
mail($to, $subject, $message, $headers);
?>

来源:http://php.net/manual/en/function。邮件.php

Just use PHP Mail and study IMF and how to build custom headers you can attach the fourth parameter, exmaple follows

<?php
// multiple recipients
$to  = '[email protected]' . ', '; // note the comma
$to .= '[email protected]';

// subject
$subject = 'Birthday Reminders for August';

// message
$message = '
<html>
   ...
</html>
';

// To send HTML mail, the Content-type header must be set
$headers  = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";

// Additional headers
$headers .= 'To: Mary <[email protected]>, Kelly <[email protected]>' . "\r\n";
$headers .= 'From: Birthday Reminder <[email protected]>' . "\r\n";
$headers .= 'Cc: [email protected]' . "\r\n";
$headers .= 'Bcc: [email protected]' . "\r\n";

// Mail it
mail($to, $subject, $message, $headers);
?>

Source: http://php.net/manual/en/function.mail.php

苯莒 2024-10-02 20:00:59

创建一个邮件队列子系统,其中可能包括 mail_queue、mail_status、mail_attachments、mail_recipients 和 mail_templates 等表...

create a mail queue subsystem which might include tables such as mail_queue, mail_status, mail_attachments, mail_recipients and mail_templates etc...

无远思近则忧 2024-10-02 20:00:59

您可以考虑 PHPMailer
http://phpmailer.worxware.com/index.php?pg=exampleasendmail

您可以添加多个收件人和一个特殊的回调函数来处理每封发送的邮件的返回消息。 (例如,请访问链接)

我不认为可以通过 php 捕获“邮件发送失败”错误邮件,除非您通过 SMTP 使用 PHPMailer,并且偶尔观察来自任何收件人的任何返回消息您的外发电子邮件集合。

You might consider PHPMailer
http://phpmailer.worxware.com/index.php?pg=exampleasendmail

You can add multiple recipients and a special callback function for handling the returning messages for every single mail sent. (for an example visit the link)

I Don't think that catching a "mail delivery failed" error mail is possible via php except that you are using PHPMailer via SMTP and once in a while watch for any returning message form any recipient in from your outgoing email collection.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文