# Framer のフォーム：フォームバックエンドを追加すべきときと、その方法

> Framer のフォームはメール、Sheets、Webhook に送れます。保存とフィルタリング、チームへの通知、送信先の追加がほしいなら Ikiform を Webhook にします。

公開日 2026-10-25 · https://ikiform.com/ja/guides/website-builder-forms/framer-forms

Framer のネイティブなフォームは、各送信をメール、Google スプレッドシート、Webhook に送り、スパム対策も内蔵しているので、受信トレイ 1 つかシート 1 つで足りるなら、ほかには何も要らないかもしれません。すべての送信をスパムの状態付きで保存し、フィールドに照らして検証し、チームにメールで送り、複数の署名付き Webhook に転送したいなら、Ikiform を Webhook の送信先に追加します。Framer は自社のサーバーから送信するので、フォームのオリジンチェックを緩めるか、Framer の署名を検証する小さなリレーを間に置いてください。

## Framer ならではの違い

このガイドのビルダーで、抜け出すべき上限がないのは Framer だけです。フォームはメール、Google スプレッドシート、任意の Webhook に送れ、疑わしい送信にフラグを付けたりブロックしたりするスパム対策も内蔵しています。Webhook は署名付きです。各リクエストには、ボディと送信 ID の HMAC-SHA256 である `Framer-Signature` ヘッダーと、`Framer-Webhook-Submission-Id` が付きます。Framer はエンドポイントが `2xx` を返さないと最大 5 回リトライし、リダイレクトはたどりません。

だからこそ、フォームバックエンドを追加する理由は正直に考えてください。それぞれがカバーする範囲は次のとおりです。

| したいこと | Framer だけ | Ikiform を Webhook にした場合 |
| --- | --- | --- |
| 受信トレイ 1 つかシート 1 つに届く送信データ | はい | はい、ダッシュボードも |
| スパムが区別された検索できる記録と、CSV エクスポート | 受信トレイかシートが記録 | はい |
| 型、長さ、選択肢のサーバー側のチェック | いいえ | はい、フィールド定義があれば |
| 複数人への通知。返信は送信者に届く | メールの送信先 | 最大 10 人の受信者、reply-to 付き |
| 1 つのフォームから複数のツールに送り、それぞれリトライ | 追加した送信先ごとに Webhook 1 つ | 署名付きエンドポイント最大 3 つ、配信ログ付きでリトライ |
| Framer、Webflow、コードのサイトで同じバックエンド | いいえ | はい |

1 行目だけで足りるなら、Framer の送信先のままにしてください。このページの残りは、足りない場合のためのものです。

## 落とし穴：Framer は自社のサーバーから送信する

Webhook の送信先では、送信するのは訪問者のブラウザーではなく Framer のサーバーです。Ikiform 側では 3 つのことが変わります。

- **オリジンがない。** サーバーからの送信には `Origin` ヘッダーがないので、**Allowed domains** を設定していると、すべての送信が違うサイトから来たものとして扱われます。
- **IP アドレスが共有される。** Ikiform はレート制限を IP アドレスごとに数えますが、すべての送信が訪問者のアドレスではなく、少数の Framer のアドレスから来ます。
- **ハニーポットがない。** Framer は作った入力欄しか送らず、Framer のフォームでは Ikiform の人間確認を実行できません。

これを受け入れるか、リレーで解決するかです。両方の方法を以下に示します。

図: Framer のフォームが Ikiform に直接送信する場合と、署名付きリレー経由で送信する場合

1. 直接：訪問者が Framer のフォームを送信し、Framer のサーバーが Origin ヘッダーなしの JSON で送り、Ikiform は Allowed domains が空で Relaxed のレート制限なら受け付けます。
2. リレー：Framer のサーバーがすべての送信にあなたのシークレットで署名し、あなたがデプロイした Worker に送ります。
3. Worker が Framer の署名を計算し直します。有効な署名のない送信には 401 が返り、先には進みません。
4. 有効な送信は、シークレットキーと、冪等性キーとしての Framer の送信 ID を付けて Ikiform に転送されるので、Framer がリトライしても 2 つ目のコピーは保存されません。

リレーの実質的なロジックは十数行です。その代わり、シークレットキーを持つのは Worker だけなので、フォームは厳格な許可ドメインを維持できます。

## 方法 1：Ikiform を Webhook にする

1. Framer で各入力欄に小文字の名前を付けます：`name`、`work_email`、`company`、`team_size`。Framer はこの名前を JSON のキーに使います。
2. フォームを選び、**Send To** の横で **Add…** > **Webhook** を選びます。エンドポイントを貼り付けます。シークレットは空のままにします。Ikiform は `Framer-Signature` を確認しません。
3. Ikiform のフォームの設定で、**Allowed domains** を空のまま、**Spam check** をオフのままにし、**Rate limit** を **Relaxed** にします。
4. 公開します。Framer が Webhook を送るのは公開したサイトからだけです。

`Webhook の URL`:

```text
https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx
```

Ikiform は送信データを保存すると `201` を返し、Framer はそれを配信完了とみなします。`422 validation_failed` は 5 回のリトライすべてで同じように失敗するので、待たずに入力欄の名前を直してください。スパムスコアはすべての送信で引き続き計算されます。リンク、スパムの言い回し、使い捨てアドレス、繰り返しです。

## 方法 2：署名付きリレー

Framer の署名を確認し、有効な送信をシークレットキー付きで転送する Cloudflare Worker をデプロイします。そうすれば Ikiform のフォームの **Allowed domains** に Framer のドメインを入れて **Reject** にでき、自分のサイト上のブラウザーか Worker 以外からの送信はすべて拒否されます。

`src/index.ts`:

```ts
interface Env {
  FRAMER_SECRET: string;
  IKIFORM_SECRET_KEY: string;
}

const ENDPOINT = "https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx";
const encoder = new TextEncoder();

async function signatureFor(secret: string, body: ArrayBuffer, submissionId: string) {
  const key = await crypto.subtle.importKey(
    "raw",
    encoder.encode(secret),
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["sign"],
  );
  const id = encoder.encode(submissionId);
  const message = new Uint8Array(body.byteLength + id.byteLength);
  message.set(new Uint8Array(body), 0);
  message.set(id, body.byteLength);
  const digest = new Uint8Array(await crypto.subtle.sign("HMAC", key, message));
  return `sha256=${[...digest].map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
}

function sameText(a: string, b: string) {
  const left = encoder.encode(a);
  const right = encoder.encode(b);
  return left.byteLength === right.byteLength && crypto.subtle.timingSafeEqual(left, right);
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== "POST") return new Response(null, { status: 405 });
    const signature = request.headers.get("framer-signature") ?? "";
    const submissionId = request.headers.get("framer-webhook-submission-id") ?? "";
    const body = await request.arrayBuffer();
    if (!submissionId || !sameText(signature, await signatureFor(env.FRAMER_SECRET, body, submissionId))) {
      return new Response("Bad signature", { status: 401 });
    }

    const response = await fetch(ENDPOINT, {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
        Authorization: `Bearer ${env.IKIFORM_SECRET_KEY}`,
        "Idempotency-Key": submissionId,
      },
      body,
    });
    if (response.ok) return new Response(null, { status: 204 });
    return new Response(await response.text(), { status: response.status });
  },
};
```

### デプロイする

`ターミナル`:

```bash
npx wrangler secret put FRAMER_SECRET
npx wrangler secret put IKIFORM_SECRET_KEY
npx wrangler deploy
```

- `FRAMER_SECRET` は、Framer の Webhook の送信先に設定するシークレットです。Framer は 32 文字以上を求め、二度と表示しないので、生成して保管し、両方の場所に貼り付けてください。
- `IKIFORM_SECRET_KEY` は、Ikiform のダッシュボードで作るシークレット API キーです。Framer には絶対に入れないでください。
- 署名は、`sha256=` に、生のボディのあとに送信 ID を続けたものの HMAC を 16 進数にした値を付けたものです。ボディはバイト列として 1 回だけ読み、その同じバイト列を確認と転送の両方に使ってください。
- `Idempotency-Key` があると、Framer のリトライには重複を保存する代わりに Ikiform の最初の応答がそのまま返ります。キーは 24 時間保持されます。
- Worker は Ikiform のステータスをそのまま返します。Framer は `2xx` 以外の応答を最大 5 回リトライするので、`429` や `5xx` にはもう一度チャンスがありますが、`422` は入力欄の名前を直すまで毎回同じように失敗します。

そのあと、Framer の Webhook をエンドポイントではなく Worker の URL に向け、シークレットを入力します。

> これですべての送信が Worker から来ます。サーバー側の版はシークレットキー付きで送信するので、Ikiform は **Allowed domains** と人間確認をスキップします。その場合はすべての送信がサーバーの IP から来るため、訪問者ごとに 1 分あたり 5 件という Standard の制限が、すべての訪問者をまとめて数えてしまいます。サーバー経由で送信するフォームでは Relaxed か Custom の[レート制限](https://ikiform.com/ja/docs/rate-limits.md)を選び、`IKIFORM_SECRET_KEY` はサーバー専用の環境変数に置いてください。

## 方法 3：Framer のフォームを使わない

コードコンポーネントは本物のフォームを描画して訪問者のブラウザーから送信するので、オリジンチェック、ハニーポット、訪問者ごとのレート制限がほかのサイトと同じように機能します。そのフォームでは Framer のビジュアルなフォーム編集が使えなくなります。コンポーネントは [Framer のドキュメントガイド](https://ikiform.com/ja/docs/guides/framer.md#code-component)にあります。

## Framer で動作を確認する

1. Framer のサイトを公開し、エディターのプレビューではなく公開したサイトでフォームを送信します。
2. ダッシュボードで **Submissions** を開きます。送ったすべてのフィールドを含む新しい送信データがあります。
3. フィールド名が入力欄の名前と完全に一致し、Framer が追加したキーがないことを確認します。
4. リレーの場合：でたらめな `Framer-Signature` を付けて Worker にリクエストを送り、`401` が返ることを確認します。
5. リレーの場合：送信データの詳細で、オリジンが空で、ステータスがスパムではなく受信済みになっていることを確認します。
6. 通知の各受信者の受信トレイを確認します。それぞれにフィールド入りのメールが届き、フォームに `email` フィールドがあれば **Reply** で送信者に返信できます。
7. Webhook を追加した場合は、**Webhooks** でエンドポイントを開きます。配信が `succeeded` になり、サーバーの応答が表示されます。

## ハマりどころ

| 問題 | 対処法 |
| --- | --- |
| すべての Webhook の送信が `origin_mismatch` でスパムになる | Framer が直接送信しているのに **Allowed domains** が設定されています。空にするか、リレーを間に置いてください。 |
| Worker が本物の送信に `401` を返す | Framer と Worker でシークレットが違うか、何かがボディをテキストとして読んで変更しています。シークレットを貼り直し、生のバイト列でハッシュを計算してください。 |
| ローンチ中に `429 rate_limited` になる | すべての送信が Framer か Cloudflare のアドレスを共有しています。**Relaxed** か Custom の制限を選んでください。 |
| `422 validation_failed` | 送信がフォームのフィールド定義に合っていません。`error.fields` に各フィールドと理由が示されます。入力欄の `name` をフィールド ID に合わせてください。 |
| `403 challenge_required` | フォームのスパムチェックが **Ikiform** に設定されていて、`fetch` の送信にトークンがありませんでした。送信のたびに `@ikiform/sdk` の `challenge(formId)` でトークンを取得するか、チェックをオフにしてください。 |

## よくある質問

### Framer のフォームにフォームバックエンドは必要ですか？

必ずしも必要ではありません。Framer は単体で、送信をメール、Google スプレッドシート、Webhook に送れます。保存されフィルタリングされた記録、サーバー側の検証、reply-to 付きのチームへの通知、1 つのフォームからリトライ付きの複数の Webhook がほしいときに Ikiform を追加してください。

### Ikiform は Framer の Webhook の署名を検証しますか？

いいえ。Ikiform は送信をオリジンかシークレット API キーで認証し、Framer のヘッダーは使いません。Framer-Signature を確認するには、Framer と Ikiform の間に、署名を検証してシークレットキー付きで転送する小さなリレーを置いてください。

### Framer の送信がスパムと判定されるのはなぜですか？

たいていは Allowed domains が設定されているからです。Framer の Webhook の送信は Framer のサーバーから Origin ヘッダーなしで届くので、どのドメインとも一致しません。一覧を空にするか、シークレットキーで送信するリレーを使ってください。

### Framer のリトライで送信データが重複しますか？

直接送信している場合、Ikiform が保存したあとに配信がリトライされると重複することがあります。リレーは Framer の送信 ID を冪等性キーとして送るので、リトライには最初の応答がそのまま返ります。

### フォームでファイルのアップロードを受け付けられますか？

まだできません。ファイルを含む送信は 422 files_not_supported で拒否されます。代わりに、共有ドライブの URL などファイルへのリンクを尋ねてください。

## その他のガイド

- [ビルダーのフォームを卒業する](https://ikiform.com/ja/guides/website-builder-forms.md): Webflow、Netlify、Carrd、Framer のフォームが上限や機能不足に当たったら、デザインしたフォームはそのままに、送信先をフォームバックエンドに変えましょう。
- [Webflow の送信上限](https://ikiform.com/ja/guides/website-builder-forms/webflow-submission-limit.md): Webflow の無料 Starter プランはサイトごとに 50 件で送信の受け付けが止まり、エクスポートしたサイトでは一切処理されないので、Form Block の送信先を Ikiform に向けましょう。
- [Carrd のフォーム](https://ikiform.com/ja/guides/website-builder-forms/carrd-forms.md): Carrd のお問い合わせフォームはメールで届けるだけで、カスタムフォームには Pro Plus が必要です。すべての送信を保存するには、Plus なら Send to URL、Standard なら HTML の埋め込みを使います。
- [Netlify Forms の上限](https://ikiform.com/ja/guides/website-builder-forms/netlify-forms-limit.md): Netlify の旧プランはサイトごとにフォーム送信を計測して自動でアップグレードし、クレジットプランはサイトを一時停止することがあるので、フォームをバックエンドに移しましょう。
