用 HTML 把联系表单发到邮箱,页面保持静态

把联系表单写在 HTML 里,消息进你已经在看的邮箱。访客留下姓名、邮箱和一段话,并留在你的网站上。你不必安装 PHP,浏览器也不会打开邮件程序。这一页上的代码片段就是那张表单。

  1. 打开一个收件箱

    注册免费账户并打开仪表盘。第一个收件箱带有访问密钥和通知邮箱。填你已经在看的那个地址。密钥可以公开。它不能改收件人,也不能读取其他消息。免费方案每个 UTC 月发送 100 条。新账户还有七天 Pro 额度,不需要银行卡。之后的 Pro 是每月 12 美元。

  2. 粘贴 HTML

    复制下面步骤之后的表单。action 是 https://getformpost.com/api/submit,method 是 POST。名为 access_key 的隐藏字段带上密钥。已登录时,输入框会填好密钥。否则先粘贴密钥再复制。标签和 CSS 仍然用你自己的。

  3. 阅读那封邮件

    部署文件,并从线上地址提交一次。字段会变成一封邮件,同一段文字也出现在仪表盘。普通的邮箱地址会成为 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 联系表单

HTML 表单发邮件,不必打开邮件客户端

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

带有访问密钥输入框和复制按钮的 HTML 表单代码

不用服务器或 PHP,把 HTML 表单发到邮箱

不用服务器或 PHP 把 HTML 表单发到邮箱,浏览器必须把数据发到一个已经能收信的地方。PHP mail() 假定那个地方和文件在同一台主机。静态主机对 contact.php 经常返回 404,或者能跑 PHP 却拒绝对外发信。就算 mail() 返回成功,共享主机也常常已在拦截名单上,凭据还会躺在网站旁边。把表单 POST 到 https://getformpost.com/api/submit,并带上访问密钥。我们收下正文,丢掉明显的机器人,存下文字,再交给你的通知邮箱。你的主机不必讲 SMTP。

静态 HTML 文件经 POST 发到接口,再进入收件箱,网站上没有 PHP

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 条会被拒绝。这是日志,不是邮件列表。

收件箱中的通知邮件,列出姓名、邮箱和留言,Reply-To 是访客

mailto 和表单后端

常见答案是一段 mailto、一段 PHP mail(),或一个浏览器 SDK。静态主机上的联系表单需要一条既不打开邮件程序、也不要求你运行 PHP 的路径。

mailto 打开邮件程序,表单后端则把 POST 送进收件箱
mailto 和表单后端
mailtoPHP mail()EmailJSFormpost
在哪里运行访客的邮件程序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 美元。额度写在免费联系表单,订阅在价格。

免费创建账户