# キーをクライアントに置かない EmailJS の代替

> お問い合わせフォームを EmailJS から移行。ブラウザー側のメールキーのリスク、コードの変更点、自動返信など失うものを解説します。

公開日 2026-10-25 · https://ikiform.com/ja/guides/send-form-to-email/emailjs-alternative

EmailJS はブラウザーからメールを送るので、サービス ID、テンプレート ID、公開キーがページの JavaScript に含まれ、誰でもコピーできます。フォームバックエンドならブラウザーにキーは要りません。フォームは URL に送信し、バックエンドがスパムを除外し、メッセージを保存し、あなたにメールで通知します。EmailJS の訪問者への自動返信はなくなるので、代わりに自分のメールツールから送ります。

## ここが違う

EmailJS が人気なのには理由があります。サーバーなしで、静的サイトから本物のメールを送れるからです。そのためにブラウザーは、サービス ID、テンプレート ID、公開キーの 3 つの値で EmailJS を直接呼び出します。これらはバンドルに含まれ、ページのソースやネットワークタブで読めます。

EmailJS 自身の FAQ には、これらをコピーした人が送れるのはあなたのテンプレートとその内容だけで、任意のメールは送れないとあります。テンプレートの固定の文面についてはそのとおりです。しかしテンプレートの変数は呼び出し側が埋めるので、コピーしたキーを使えば、任意の名前、メッセージ、リンクを入れたテンプレートを、クォータの許す限り何度でも送れます。さらに、訪問者のアドレス宛の自動返信のようにテンプレートの **To** が変数なら、呼び出し側が受信者まで選べます。

図: ブラウザーのメールキーで送れるのは誰か、フォームのエンドポイントでは誰か

1. ブラウザーのメール SDK では、送信に必要なものがすべてページにあります。どこか別のスクリプトが ID とキーをコピーし、独自の文面であなたのテンプレートをクォータまで送れます。
2. テンプレートの受信者が変数で決まるなら、コピーしたキーで受信者も選べます。
3. フォームのエンドポイントでは、ページにあるのはフォーム ID だけです。送信はスパムを除外され、レート制限され、メールはダッシュボードで設定した受信者にしか届きません。

フォーム ID も秘密ではありません。違いは、他人がそれで何をできるかです。できるのはフォームの送信であって、あなたのメールの送信ではありません。

EmailJS には、キーを使えるドメインの制限やテンプレートへの CAPTCHA の追加など、これを絞り込む設定があります。使い続けるなら、プランに含まれているものを確認してオンにしてください。以下の比較は設計についてのもので、サービスの良し悪しの判定ではありません:

|   | EmailJS | Ikiform |
| --- | --- | --- |
| ブラウザーが持つもの | サービス ID、テンプレート ID、公開キー | フォーム ID |
| 受信者を決めるもの | テンプレート（変数を読むこともある） | ダッシュボードの設定だけ |
| スパムの除外 | 追加すればテンプレートごとに CAPTCHA | すべてのフォームにハニーポット、スパムスコア、レート制限。人間確認も任意で追加可能 |
| 送信後のメッセージ | 受信トレイの中 | 受信トレイに加え、ダッシュボード、CSV エクスポート、API |
| 訪問者へのメール | あり。自動返信テンプレートで | いいえ |
| 独自のメールデザイン | あり。エディターでテンプレートを作成 | なし。件名 `New submission: <form name>` の固定レイアウト |

自動返信やブランドに合わせたメールが目的なら、EmailJS か、メール API を使う自前のサーバーのほうが向いています。メッセージを安全に受信トレイへ届け、どこかに残しておきたいなら、フォームバックエンドが向いています。

## フォームを移行する

EmailJS のお問い合わせフォームのチュートリアルをもとにした、React の典型的な EmailJS フォーム:

`ContactForm.tsx の変更前`:

```tsx
import emailjs from "@emailjs/browser";
import { type FormEvent, useRef, useState } from "react";

export function ContactForm() {
  const form = useRef<HTMLFormElement>(null);
  const [status, setStatus] = useState("");

  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    if (!form.current) return;
    try {
      await emailjs.sendForm("service_abc123", "template_xyz789", form.current, {
        publicKey: "aBcDeFgHiJkLmNoPq",
      });
      setStatus("Message sent.");
    } catch {
      setStatus("Couldn't send your message.");
    }
  }

  return (
    <form ref={form} onSubmit={onSubmit}>
      <input name="user_name" required />
      <input name="user_email" type="email" required />
      <textarea name="message" required />
      <button>Send</button>
      <p role="status">{status}</p>
    </form>
  );
}
```

同じフォームを `@ikiform/sdk` の `submit()` で Ikiform に送信する版:

`ContactForm.tsx の変更後`:

```tsx
import { submit } from "@ikiform/sdk";
import { type FormEvent, useState } from "react";

const FORM_ID = "frm_xxxxxxxxxxxxxxxx";

export function ContactForm() {
  const [status, setStatus] = useState<"idle" | "sending" | "sent">("idle");
  const [fields, setFields] = useState<Record<string, string[]>>({});
  const [error, setError] = useState("");

  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    setStatus("sending");
    setFields({});
    setError("");
    const result = await submit(FORM_ID, new FormData(event.currentTarget));
    if (result.ok) {
      setStatus("sent");
      return;
    }
    setFields(result.error.fields ?? {});
    setError(result.error.code === "network_error" ? "You're offline. Try again." : result.error.message);
    setStatus("idle");
  }

  if (status === "sent") return <p role="status">Message sent. We'll reply by email.</p>;

  return (
    <form onSubmit={onSubmit}>
      <label htmlFor="user_name">Name</label>
      <input id="user_name" name="user_name" required />
      <label htmlFor="email">Email</label>
      <input id="email" name="email" type="email" required />
      {fields.email ? <p role="alert">{fields.email[0]}</p> : null}
      <label htmlFor="message">Message</label>
      <textarea id="message" name="message" required />
      {fields.message ? <p role="alert">{fields.message[0]}</p> : null}
      <input name="_gotcha" type="text" tabIndex={-1} autoComplete="off" hidden />
      {error && !Object.keys(fields).length ? <p role="alert">{error}</p> : null}
      <button disabled={status === "sending"}>{status === "sending" ? "Sending" : "Send"}</button>
    </form>
  );
}
```

- `@emailjs/browser` と 3 つの ID は不要になります。パッケージをアンインストールし、新しいフォームが動いたら EmailJS のテンプレートかサービスを削除して、キャッシュされた古いページから送信されないようにします。
- `user_email` は `email` になります。EmailJS は名前を気にしませんが、Ikiform は `email` という名前のフィールドを返信先に使います。通知は届いた順にフィールドを並べるので、他の名前はそのままで構いません。
- `submit()` は例外を投げません。エラーのエンベロープを返すので、フォームの定義によるフィールドエラーがそれぞれの入力欄の下に表示されます。
- ハニーポットは新しく加わったものです。EmailJS にはありませんでした。この hidden の入力欄は、設定なしで単純なボットを捕まえます。
- **Allowed domains** に自分のドメインを追加して、他のサイトからの送信がスパムとして扱われるようにします。

> 人間確認を使いますか？ フォームの **Spam check** を **Ikiform** に設定し、`submit` の前に `challenge(FORM_ID)` を呼んで、トークンを `turnstileToken` として渡します。[React ガイド](https://ikiform.com/ja/docs/guides/react.md)に例があります。

## 自動返信を使っていた場合

Ikiform はフォームを入力した本人にはメールを送りません。通知メールが届くのはあなたとチームだけなので、自動返信、確認メール、ダブルオプトインはありません。それらは [Webhook](https://ikiform.com/ja/docs/webhooks.md) をきっかけに、自分のメールツールから送ってください。Resend や Postmark など、サーバーから操作する送信サービスなら、`submission.created` の Webhook ごとに、ブラウザーではなく信頼できるペイロードにある訪問者のアドレス宛に送れます。[複数の受信者のガイド](https://ikiform.com/ja/guides/send-form-to-email/multiple-recipients.md#route)に、応用できるハンドラーがあります。

本当に必要か考えてみてください。お問い合わせフォームなら、ページに表示するお礼のメッセージで送信できたことは伝わりますし、1 時間後のあなたからの本物の返信はテンプレートに勝ります。

## 動作を確認する

1. フォームの **Allowed domains** に自分のサイトのドメインと、開発中は `localhost` を追加します。空にしておくと、どこからの送信でも受け付けます。
2. ビルドしたサイトで `emailjs` と古いサービス ID を検索します。何も一致しないはずです。
3. ページのフォームに入力して送信します。自分のサンクスページか、Ikiform のサンクスページに移動します。
4. 通知の各受信者の受信トレイを確認します。それぞれにフィールド入りのメールが届き、フォームに `email` フィールドがあれば **Reply** は送信者宛になります。
5. ハニーポットを埋めて `_gotcha=bot` で送信します。成功の応答は返りますが、送信データはスパムとして保存され、メールも Webhook も送られません。

## うまくいかないとき

| 問題 | 対処法 |
| --- | --- |
| 返信がまだ Ikiform 宛になる | フィールドがまだ `user_email` のままです。`email`、または `email`、`email_address`、`your_email`、`reply_to`、`contact_email` のどれかに名前を変えてください。 |
| `422 validation_failed` | 送信がフォームのフィールド定義に合っていません。`error.fields` に各フィールドと理由が示されます。入力欄の `name` をフィールド ID に合わせてください。 |
| `403 challenge_required` | フォームのスパムチェックが **Ikiform** に設定されていて、`fetch` の送信にトークンがありませんでした。送信のたびに `@ikiform/sdk` の `challenge(formId)` でトークンを取得するか、チェックをオフにしてください。 |
| `403 origin_not_allowed` が返るか、すべての送信がスパムになる | ページのドメインがフォームの **Allowed domains** にありません。追加し、ローカルでのテスト用に `localhost` も追加してください。登録したドメインのサブドメインは許可されます。 |

## よくある質問

### Ikiform は EmailJS より安いですか？

量とプランによりますし、どちらも変わっていくので、最新の料金ページを比較してください。Ikiform に無料プランはなく、Pro か 1 回払いのパックで送信件数ごとに課金されます。

### EmailJS の公開キーを公開しても安全ですか？

EmailJS によれば、コピーされたキーで送れるのはあなたのテンプレートだけで、独自のメールは送れません。ただし、テンプレートの変数は呼び出し側が埋め、受信者が変数ならそれも選べます。使い続けるなら、キーを自分のドメインに限定し、CAPTCHA を追加してください。

### Ikiform はブラウザーに API キーが必要ですか？

いいえ。ブラウザーのフォームは、キーなしでフォームの URL に送信します。シークレットキーは自分のサーバーから送信するためだけのもので、サーバー側の環境変数に置きます。

### Ikiform からフォームを送信した本人にメールを送れますか？

いいえ。Ikiform がメールを送るのはあなたとチームで、送信者には送らないので、自動返信や確認メールはありません。Webhook でメールツールに送り、そこから確認メールを送ってください。

### EmailJS のフィールド名をそのまま使えますか？

はい。ただしメールのフィールドは別で、通知への返信が訪問者に届くよう email という名前にしてください。Ikiform は送信されたどんなフィールド名でも保存します。

## その他のガイド

- [フォームをメールに送る](https://ikiform.com/ja/guides/send-form-to-email.md): メールサーバーなしで、すべての送信データを受信トレイに届けます。mailto: と PHP の mail() がうまくいかない理由と、スパムを除外して返信もできる構成を解説します。
- [Next.js](https://ikiform.com/ja/guides/send-form-to-email/nextjs.md): Server Action で Next.js のフォームをメールで送る 2 つの方法を公平に比較します。Resend や Nodemailer で自分で送るか、フォームバックエンドに転送するかです。
- [受信トレイから返信](https://ikiform.com/ja/guides/send-form-to-email/reply-to.md): フォームの通知メールで Reply を押すと、フォームに入力した人宛てになるようにします。使えるフィールド名、_replyto を使う場面、返信が迷子になる理由を説明します。
- [PHP なしで](https://ikiform.com/ja/guides/send-form-to-email/without-php.md): mail() を呼ぶ contact.php スクリプトを、ホストされたエンドポイントに送信するフォームに置き換えます。フィールド名、サンクスページ、返信はそのままです。
- [Gmail へ](https://ikiform.com/ja/guides/send-form-to-email/gmail.md): mailto: リンクもアプリパスワードも Apps Script も使わずに、フォームの送信データを Gmail で受け取り、ラベルを付け、迷惑メールを避け、Gmail から返信します。
- [複数の受信者](https://ikiform.com/ja/guides/send-form-to-email/multiple-recipients.md): 1 つのフォームをチーム全員にメールで送ったり、トピックごとに適切な担当者に振り分けたり。ただし、誰に送るかをフォーム自身には決めさせません。
