---
type: explanation
title: 不用后端也能验证
summary: 托管验证是什么、它给谁用、以及 Caputchin 如何核验一次提交并把它转发到一个 webhook 或邮件，让你完全不用跑后端。
---

一个 Caputchin 令牌在有东西确认它之前什么都证明不了。通常那个东西是 [你自己的后端](/docs/integration-guides/verify-on-your-backend)，它调用 `/siteverify` 并决定是否信任该请求。托管验证把那一步挪到 Caputchin 上：你的表单提交到一个 Caputchin 转发器，Caputchin 核验令牌、丢弃任何失败的、并只把已核验的提交转发到一个你选定的目标。

它和后端检查是同一个信任决定，只是在 Caputchin 这一侧、而非你这一侧运行。要点是没有 `/siteverify` 调用要写，也没有密钥要留在一台服务器上，因为根本没有服务器。

托管验证是一个付费功能，在 Alpha 及以上可用。

## 它给谁用

托管验证的存在，是为了那种“仅仅为了核验一个令牌就跑一个后端”超出项目所需的情况。

| 你是 | 为什么合适 |
|---|---|
| 一个静态站点（Webflow、Framer、CDN 上的纯 HTML） | 没有服务器来接收表单或调用 `/siteverify`。 |
| 一个无代码搭建工具（Wix、Squarespace、Carrd） | 你加不了服务端验证代码。 |
| 一个独立开发者或小团队 | 一个联系或注册表单不值得为它立起并运营一个后端。 |
| 已经在跑一个后端，但不是为这个表单 | 你可以把一个表单指向转发器，而让你技术栈的其余部分原封不动。 |

如果你确实跑一个后端、并想在那里核验，那就改用 [在你的后端核验](/docs/integration-guides/verify-on-your-backend)。两者是互斥的选择，而非叠加的层。

## 它如何运作

```mermaid
sequenceDiagram
  autonumber
  participant U as 访客
  participant B as 浏览器（组件）
  participant F as Caputchin 转发器
  participant D as 你的目标（webhook 或邮件）

  U->>B: 填好表单并提交
  B->>F: POST 表单字段加上 caputchin 令牌
  Note over F: 核验令牌，然后把它从载荷里去掉
  alt 令牌有效
    F->>D: 投递这次提交
    D-->>F: 确认
    F-->>B: 200 带投递结果
  else 令牌缺失或无效
    F-->>B: 被拒绝，什么都不投递
  end
```

转发器只在投递它所花的那一瞬间把提交保留在内存里。提交和投递之间没有存储，而 `caputchin-token` 字段在载荷到达你的目标之前就被剥离。

## 目标

你按站点密钥配置其中一个或两个。两个都启用是一种常见套路：发到一个 webhook 做处理，再给自己邮一份副本。

| 目标 | 你收到什么 |
|---|---|
| **Webhook** | 一个 JSON `POST`，携带你的表单字段加上 Caputchin 的验证元数据。 |
| **邮件** | 一封朴素的邮件，带表单字段和一个注明 Caputchin 已核验该提交的页脚。 |

投递不签名。转发器 URL 的保密性即为一个 webhook 调用的身份验证，所以把这个 URL 留在公开仓库和客户端代码之外。[设置教程](/docs/hosted-verification/set-up) 展示确切的 webhook 载荷。

## 什么被保密

托管验证遵循和 Caputchin 其余部分同样的无访客数据姿态：

- **提交从不被存储。** 它们在转发期间存活于进程内存里，然后就没了。如果你想要一份记录，就在 webhook 那一端建你自己的。
- **不采集关于提交者的任何数据。** 无 IP、无 User-Agent、无指纹、无追踪。
- **遥测仅为聚合。** Caputchin 计数投递的成功和失败，好让你看到你的目标是否健康，而绝不计数任何提交的内容。见 [统计](/docs/hosted-verification/statistics)。

## 一个客户提供的 URL 如何被保障安全

一个 Caputchin 的服务器将会调用的 URL，是一个典型的服务端请求伪造风险。转发器会拒绝任何主机是私有、回环、链路本地或云元数据地址的 webhook URL，在你保存它时拒绝，并在每次投递之前再次拒绝。被阻止的类别包括：

| 类别 | 例子 |
|---|---|
| 回环与未指定 | `127.0.0.1`、`0.0.0.0`、`::1` |
| 私有（RFC 1918） | `10.x.x.x`、`172.16.x.x` 到 `172.31.x.x`、`192.168.x.x` |
| 链路本地与云元数据 | `169.254.x.x`，尤其是元数据端点 `169.254.169.254` |
| 运营商级 NAT | `100.64.x.x` 到 `100.127.x.x` |
| 多播与保留 | `224.x.x.x` 及以上 |
| 内部主机名 | `localhost`，以及任何以 `.local`、`.localhost` 或 `.internal` 结尾的主机 |
| 私有 IPv6 | 链路本地（`fe80::/10`）和唯一本地（`fc00::/7`）地址 |

那些试图伪装一个被阻止地址的编码也会被逮住：一个十进制整数主机如 `http://2130706433/`，或一个十六进制主机如 `http://0x7f000001/`（两者都意为 `127.0.0.1`），都会被拒绝。只接受 `http` 和 `https` URL；任何其他协议都被拒。转发器还拒绝跟随重定向，所以一个 webhook 端点无法把调用弹到一个内部目标上。一个被阻止的 URL 以一个通用错误、而非一个具体错误的形式浮现，所以它无法被用来探测一个网络。因此你的 webhook 必须住在一个公开的 `https` URL 上。

## 什么被有意略去

- **没有提交收件箱。** 没有存储的提交历史可供在仪表盘里浏览。
- **没有第一方的 Discord、Slack、Telegram 或 SMS 适配器。** 只有 webhook 和邮件。一个 webhook 可以在你这一侧扇出到那些里的任何一个。
- **没有文件上传。** 转发器接受文本字段；一个携带文件的提交会被拒绝。
- **没有载荷变换。** 表单提交什么，你的目标就收到什么，外加验证元数据。

## 另见

- [设置托管验证](/docs/hosted-verification/set-up)：一篇先把它接到一个测试收件箱的演练。
- [托管验证统计](/docs/hosted-verification/statistics)：读投递健康度。
- [在你的后端核验](/docs/integration-guides/verify-on-your-backend)：当你确实跑一台服务器时的那个替代方案。
