用 HTML 把联系表单发到邮箱,页面保持静态
把联系表单写在 HTML 里,消息进你已经在看的邮箱。访客留下姓名、邮箱和一段话,并留在你的网站上。你不必安装 PHP,浏览器也不会打开邮件程序。这一页上的代码片段就是那张表单。
打开一个收件箱
注册免费账户并打开仪表盘。第一个收件箱带有访问密钥和通知邮箱。填你已经在看的那个地址。密钥可以公开。它不能改收件人,也不能读取其他消息。免费方案每个 UTC 月发送 100 条。新账户还有七天 Pro 额度,不需要银行卡。之后的 Pro 是每月 12 美元。
粘贴 HTML
复制下面步骤之后的表单。action 是 https://getformpost.com/api/submit,method 是 POST。名为 access_key 的隐藏字段带上密钥。已登录时,输入框会填好密钥。否则先粘贴密钥再复制。标签和 CSS 仍然用你自己的。
阅读那封邮件
部署文件,并从线上地址提交一次。字段会变成一封邮件,同一段文字也出现在仪表盘。普通的邮箱地址会成为 Reply-To,回复会回到访客。sent 表示投递已被接受。logged 表示已保存但邮件还没接通。failed 表示保存成功但发送失败。
下面的 HTML 会跟着变。不用改代码。
HTML
<form action="https://getformpost.com/api/submit" method="POST">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
<label>
姓名
<input type="text" name="name" required>
</label>
<label>
邮箱
<input type="email" name="email" required>
</label>
<label>
留言
<textarea name="message" required></textarea>
</label>
<input type="text" name="botcheck" tabindex="-1" autocomplete="off" style="display:none" aria-hidden="true">
<button type="submit">发送</button>
</form>把 HTML 表单发到邮箱,文件仍由你托管
HTML 表单发到邮箱,从你已经在改的文件开始。常见的是 contact.html,或 index.html 里的一节,放在 GitHub Pages、Cloudflare Pages、Netlify 或对象存储上。主机只要把文件发出去。部署后从线上地址提交一次。有的静态主机会拦截发往自己域名的 POST。这张表单发往我们的主机,所以那层拦截用不上。不要把邮箱密码放进仓库。页面留在仓库里。别处只跑 POST 处理。

HTML 表单发邮件,不必打开邮件客户端
写成 mailto 的链接看起来很短,然后就会失败。点击会打开邮件程序,或者在没有邮件程序的手机上什么都不发生。收件人和正文待在 URL 里,长留言会被截断,访客也能改掉地址。很多人根本不会再按发送。过滤器也不信任从几十个私人账户涌来的信。换成真正的表单。按钮提交到接口,访客看到感谢页,你在指定的邮箱里收到一封信。访客不必打开写信窗口。

不用服务器或 PHP,把 HTML 表单发到邮箱
不用服务器或 PHP 把 HTML 表单发到邮箱,浏览器必须把数据发到一个已经能收信的地方。PHP mail() 假定那个地方和文件在同一台主机。静态主机对 contact.php 经常返回 404,或者能跑 PHP 却拒绝对外发信。就算 mail() 返回成功,共享主机也常常已在拦截名单上,凭据还会躺在网站旁边。把表单 POST 到 https://getformpost.com/api/submit,并带上访问密钥。我们收下正文,丢掉明显的机器人,存下文字,再交给你的通知邮箱。你的主机不必讲 SMTP。
HTML 联系表单发到邮箱
第一版用三个字段:name、email、message。这些名字会变成邮件里的行。多出来的文本字段也会转发。文件字段会被忽略。代码片段里有一个名为 botcheck 的字段,用 CSS 隐藏,并移出 Tab 顺序。人会跳过它。把每个输入都填上的脚本也会填它,接口会假装成功,然后丢掉这次提交。什么都不保存。同一个 IP 和同一把密钥大约每五秒只能成功一次。JavaScript 不是必需的。fetch 如果发送 Accept: application/json,或把 _reply 设为 json,会得到状态对象,而不是新页面。

收件箱里的邮件有什么
主题来自 _subject、subject,或包含收件箱名称的默认值。其余字段按到达顺序列出。访问密钥、蜜罐和回复标记不会抄进邮件。一个普通的 email 或 from 地址成为 Reply-To,其中的换行会被拒绝,避免追加邮件头。仪表盘保存同样的字段,状态是 sent、logged、failed 或 locked。locked 是免费方案在一个 UTC 月里超过 100 条:记录还在,字段在升级前隐藏,字段邮件不会发出。Pro 每月 12 美元,5000 条提交,20 个收件箱。超过 5000 条会被拒绝。这是日志,不是邮件列表。

mailto 和表单后端
常见答案是一段 mailto、一段 PHP mail(),或一个浏览器 SDK。静态主机上的联系表单需要一条既不打开邮件程序、也不要求你运行 PHP 的路径。
| mailto | PHP mail() | EmailJS | Formpost | |
|---|---|---|---|---|
| 在哪里运行 | 访客的邮件程序 | PHP 主机 | 浏览器里的 JavaScript | 表单接口 |
| 你的网站要服务器吗 | 不要 | 需要 PHP | 不要 | 不要 |
| 访客看到什么 | 写信窗口 | 你的页面(如果 mail() 成功) | 脚本跑完后的页面 | 你的页面,然后是感谢页 |
| 页面里的秘密 | 收件人地址 | 密码留在服务器 | 浏览器里的公钥 | 只指向一个收件箱的访问密钥 |
| 会进你的邮箱吗 | 只有他们再按发送 | 主机允许 mail() 时 | 经他们的 API | 邮件,外加仪表盘副本 |
| 垃圾控制 | 没有 | 你自己写 | 他们的额度和规则 | 蜜罐和短暂停顿 |
消息没有到达的时候
看起来毫无反应的表单,通常已经完成了 POST。缺口在接口和你正在看的邮箱之间。先做下面四项检查,再改表单。
收件箱里什么都没有
核对通知邮箱,再用隐私窗口提交一次。sent 表示服务商接受了这封信。logged 表示投递还没接通,文字仍在列表里。账户刚建好时这是正常的,不该留在公开网站上。failed 表示保存成功但发送返回了错误。确认页面里的密钥是当前这把。轮换密钥会立刻停用旧的。
查看垃圾邮件文件夹
新发件人的第一封信经常进垃圾箱。在判定表单坏了之前,先搜收件箱名称和访客地址。把一封真信移回收件箱,后面的信会跟着来。请别人提交一次。从没见过这个发件人的服务商,比你自己给自己写的便条更严格。免费方案会在通知里加一行 Formpost。那一行不是被过滤的原因。新的发件人地址才是常见原因。
跳转和感谢页
普通提交只有在你把感谢页 URL 保存在收件箱上之后才会跳转。地址必须是 https,或本机的 http,并且必须是登录时保存的那一个。POST 里的 redirect 字段不能把浏览器带到别处。没有保存 URL 时,访客看到一页简短的托管感谢页,而不是白屏或原始 JSON。请求 JSON 的 fetch 不会跳转。它收到 success、一句短消息和一个 id。用真实的点击看一次访客会看到的页面。
蜜罐挡住了真人
如果有人说提交了,你却没有记录,先看蜜罐。botcheck 必须是空的。自动填充,或仍留在 Tab 顺序里的 CSS,可能把它填上。接口会假装成功并丢掉提交,看起来就像邮件丢了。把它隐藏,tabindex 设为 -1,并关掉自动完成。代码片段已经这样做。填满每个输入的脚本会被丢掉。亲手打字的人不会。
接着读
父页面表单发到邮箱讲请求本身:内容类型、Reply-To,以及锁住的跳转。首页是带同一段代码的网站联系表单。额度与每月 12 美元的 Pro 在价格和免费联系表单。同一件事的短文收在博客。页面已经写好、只差一个收件箱时,用这一页。
常见问题
下面的问题对应第一位真实访客之后常见的情况:收件箱是空的、信在垃圾箱、感谢页、以及一个过于敏感的蜜罐。
用免费方案粘贴
创建账户,第一张表单就可以复制。免费方案每个 UTC 月发送 100 条,一个收件箱。需要 5000 条或多于一个网站时,Pro 是每月 12 美元。额度写在免费联系表单,订阅在价格。
免费创建账户