HTML 表单用 PHP 发邮件
一段 HTML 表单配 PHP 发信脚本实际需要什么,为什么多数静态主机跑不了,以及把同一张表单指到托管接口。
教程里最老的答案是一个叫 send.php 的文件调用 mail(),表单的 action 指向它。在运行 PHP 且允许对外发信的主机上,它能工作。很多现在的网站不在那种主机上。这篇给出脚本、会安静失败的部分,以及不用 PHP 的同一张表单。

人们复制的脚本
<?php
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';
$message = $_POST['message'] ?? '';
$to = 'you@example.com';
$subject = '联系表单';
$body = "姓名: $name\n邮箱: $email\n\n$message";
$headers = "From: you@example.com\r\nReply-To: $email";
mail($to, $subject, $body, $headers);
header('Location: /thanks.html');
表单是普通 HTML。
<form action="/send.php" method="POST">
<input name="name" />
<input type="email" name="email" />
<textarea name="message"></textarea>
<button type="submit">发送</button>
</form>
在传统虚拟主机上这能投递,直到主机的发信 IP 被列入垃圾名单,邮件开始消失。mail() 返回 true 只表示本机邮件传输代理收下了消息,不表示有人读到了它。

复制版会在哪里坏掉
回复地址取自访客并放进邮件头。包含换行的值可以追加你没写的头。应当去掉换行,并且只在它是单个地址时接受。上面的例子两件都没做。
没有蜜罐,没有提交间隔,也没有每月上限。第一个找到 send.php 的脚本会投一整夜。主机会随后限制整个账户,包括你想要的邮件。

脚本信任每一个字段。如果以后加上文件上传,需要大小上限和类型白名单。例子什么都不存,所以失败的 mail() 是看不见的。访客看到了跳转,你看到空收件箱。
静态主机切得更干净。文件主机、Pages 主机或对象存储上没有 PHP 进程。把 send.php 放在 index.html 旁边不会凭空出现一个。表单 action 会 404,或者主机把文件当成下载。这通常就是现代宣传站上“用 PHP 把表单发到邮箱”的结局。

同一张表单,没有这段脚本
把 action 指到 https://getformpost.com/api/submit,并加上仪表盘里的隐藏 access key。字段名保持不变。加一个名为 botcheck 的隐藏蜜罐。把感谢页地址存在收件箱里,而不是调用 header()。接口会拒绝表单自己指定的跳转,去掉邮件头里的换行,并在发信失败时仍然保存这一行。
下面的 HTML 会跟着变。不用改代码。
HTML
<form action="https://getformpost.com/api/submit" method="POST">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input
type="text"
name="botcheck"
style="display:none"
tabindex="-1"
autocomplete="off"
/>
<input name="name" required />
<input type="email" name="email" required />
<textarea name="message" required></textarea>
<button type="submit">发送</button>
</form>HTML 仍由你写。邮件传输代理不用你运维。网站联系表单 就是这个产品。表单发到邮箱 把投递规则放在一处。如果是脚本而不是浏览器在 POST,提交表单到邮箱 是请求约定。

PHP 仍然合适的时候
表单必须做接口不会做的事时,保留 PHP:写入你已经在跑的数据库、收款,或把核对过的文件送进内部流程。这时从服务器调用邮件 API,校验地址,并保留蜜罐。不要在共享主机上调用 mail() 然后指望它。
不要只因为一篇旧文章用了 PHP 就留着它。如果网站是一夹 HTML,一个 PHP 文件就是你现在还得打补丁的第二套系统。一个接口、一把 key,以及消息似乎丢失时能打开的仪表盘,是更小的系统。
免费上限是每个 UTC 月 100 条。付费上限在 免费联系表单 页。PHP 脚本没有上限,直到主机强加一个,那是更糟的了解限额的时刻。删掉 send.php 之前,如果想看托管表单的逐字段版本,读 HTML 联系表单发到邮箱。