ニュースレター登録フォーム2 分で読めます
サーバーなしで、静的サイトにメール登録フォームを置く
Hugo、Jekyll、Eleventy、Astro の静的サイトにニュースレター登録フォームを追加し、メールツールの API キーをページに入れず、確認はツールで行います。
静的サイトには送信先となる自分のサーバーがないので、フォームは読者のブラウザーから直接 Ikiform に送信します。メールツールの API キーはサイトに一切入れないでください。ブラウザーが知っているのは公開されている Ikiform のエンドポイントだけで、別の場所でホストしたリレーが購読者を追加します。フォームはパーシャルやインクルードとして一度だけ書き、設置場所ごとに異なる source を渡し、再読み込みなしの版には小さなスクリプトを 1 つ追加します。
ここが違う
静的サイトとは、ホスト上のファイルです。GitHub Pages、Netlify、Cloudflare Pages、S3 バケットなどです。読者がフォームを送信しても、自分で書いたものがサーバーで動くことはありません。そのため、ページからメールツールの API を呼び出すというよくある近道は使えません。ブラウザーで実行されるものは、誰でも読めるからです。
メールツールの API キーが、購読者 1 人の追加だけに限定されていることはまれです。購読者を追加できるキーは、たいていリスト全体の閲覧、エクスポート、削除もできます。静的サイトの JavaScript に入れると全訪問者に配布され、開発者ツールを開いた人なら誰でも手に入ります。
- キーがページにあると、すべての訪問者がそれをダウンロードし、誰でもそのキーでメールツールの API を呼び出せます。
- リレーを使えば、ページが知っているのは Ikiform のエンドポイントだけです。これはフォームの送信しか受け付けません。
- Ikiform がスパムを除外し、署名付きの Webhook をリレーに送ります。
- 静的サイトの外でホストされたリレーが API キーを保持し、購読者を追加します。
フォームは一度だけ書く
ニュースレターのフォームは、フッター、各記事の末尾、ニュースレターのページなど、いくつものテンプレートに登場します。source の値を受け取るパーシャルにすれば、設置場所ごとに自分自身を記録できます。マークアップはどのジェネレーターでも同じで、変わるのは source のテンプレート構文だけです。
<form data-newsletter action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
<input name="email" type="email" required autocomplete="email" placeholder="you@example.com" aria-label="Email" />
<label><input name="consent" type="checkbox" value="true" required /> Email me the newsletter. I can unsubscribe at any time.</label>
<input type="hidden" name="source" value="{{ .source | default "footer" }}" />
<input type="hidden" name="_redirect" value="https://example.com/subscribed/" />
<input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
<button>Subscribe</button>
<p role="status" hidden></p>
</form>次に、設置場所の名前を付けてテンプレートに入れます。
{{ partial "newsletter.html" (dict "source" "post") }}- Astro では
<Newsletter source="post" />を使います。静的な Astro のビルドでは Actions を実行できないので、このコンポーネントも他と同じく直接 Ikiform に送信します。 - Ikiform のフォームのフィールド定義に、
sourceを最大長の短いテキストとして追加します。フィールドを定義している場合、定義にない入力欄はunknown_fieldで拒否されます。 _redirectは、フォームの Allowed domains のいずれかにあるhttps:の URL でなければなりません。受信トレイを確認するよう伝える 1 行を載せた/subscribed/ページを作ってください。
ページ上のすべてのフォームに 1 つのスクリプト
記事のページには、記事の後とフッターの 2 つのニュースレターフォームがあることがあります。このスクリプトは data-newsletter の付いたすべてのフォームを処理し、1 つが成功するとすべてを置き換えるので、少し下でもう一度購読を求められることはありません。ベースレイアウトから一度だけ読み込んでください。
for (const form of document.querySelectorAll("form[data-newsletter]")) {
const status = form.querySelector('[role="status"]');
const button = form.querySelector("button");
form.addEventListener("submit", async (event) => {
event.preventDefault();
button.disabled = true;
status.hidden = false;
status.textContent = "Subscribing";
try {
const response = await fetch(form.action, {
method: "POST",
headers: { Accept: "application/json" },
body: new FormData(form),
});
const result = await response.json();
if (result.ok) {
for (const other of document.querySelectorAll("form[data-newsletter]")) {
other.replaceChildren(Object.assign(document.createElement("p"), {
textContent: "Almost done. Check your inbox for a confirmation link.",
}));
}
return;
}
if (response.status === 429 && response.headers.has("Retry-After")) {
status.textContent = "Too many tries from this network. Wait a minute and try again.";
} else {
const errors = result.error.fields ?? {};
status.textContent = errors.email?.[0] ?? errors.consent?.[0] ?? result.error.message;
}
} catch {
form.submit();
return;
}
button.disabled = false;
});
}- 厳しいネットワークフィルターの内側などで
fetchが失敗した場合、スクリプトはform.submit()にフォールバックします。これは通常の方法で送信し、/subscribed/に着地します。 429には専用のメッセージを表示します。同じオフィスやキャンパスのネットワークにいる読者は IP を共有しているので、訪問者ごとの制限にまとめて達することがあります。- フォームは JavaScript がオフでも動きます。スクリプトは応答の表示方法を変えるだけです。
JavaScript がオフの読者は /subscribed/ に着地するので、そのページでスクリプトが他の人にしていることを代わりに行いましょう。確認メールが送られていることを伝え、探すべき送信者名を示し、迷惑メールフォルダーにも触れ、ブログへのリンクを置きます。ジェネレーターで他のページと同じように作る普通のページで、あとでメールツールの確認リンクの遷移先にすることもできます。
リレーをどこに置くか
リレーには、コードを実行できる公開の HTTPS URL が必要です。静的ホストによって、提供されている場合とされていない場合があります。
| 静的ホスト | リレーの選択肢 |
|---|---|
| GitHub Pages, S3 | 関数なし。カスタムドメインの Cloudflare Worker か、自動化ツールを使います |
| Netlify | サイトのドメインの Netlify Function、または上記のいずれか |
| Cloudflare Pages | カスタムドメインの別の Worker。Ikiform は pages.dev と workers.dev の URL を拒否します |
| Vercel | 自分のドメインの関数。静的な出力を API ルートと並べて置けます |
各メールツール用のリレーのコードは、Kit、MailerLite、beehiiv のページにあります。コードを一切書かないなら、Webhook を Zapier か n8n に向け、そのメールツール用のアクションを使ってください。URL のルールは Webhook のドキュメントにあります。
localhost でテストしてください。サイトが Content Security Policy を送っている場合
多くの静的ホストでは、Netlify や Cloudflare Pages の _headers のように、ファイルからヘッダーを設定できます。Content-Security-Policy を送っている場合、ポリシーが Ikiform を許可していなければブラウザーはフォームをブロックします。form-action はプレーンな送信を、connect-src はスクリプトの fetch をカバーします。
Content-Security-Policy: default-src 'self'; form-action 'self' https://ikiform.com; connect-src 'self' https://ikiform.comブロックされた送信はブラウザーのコンソールに CSP エラーとして表示され、Ikiform には届かないので、ダッシュボードにも何も表示されません。
動作を確認する
- 本番のドメインと
localhostを Allowed domains に追加します。 - サイトをビルドして記事のページを開きます。両方のフォームがあり、ページのソースではそれぞれ別の
sourceの値を持っています。 - 記事のフォームから自分のアドレスで購読します。両方のフォームが、受信トレイを確認するよう伝える 1 行に変わります。
- JavaScript をオフにしてフッターから購読します。
/subscribed/に着地します。 - ダッシュボードで Submissions を開きます。2 件の登録の
sourceはpostとfooterになっています。 - ビルドしたサイトでメールツールの API キーを検索します。見つかりません。
ハマりどころ
| 問題 | 対処法 |
|---|---|
| ダッシュボードに何もなく、コンソールに CSP エラーがある | Content-Security-Policy の form-action と connect-src に https://ikiform.com を追加してください。 |
source で unknown_field の 422 | フォームのフィールド定義に source を追加するか、非表示の入力欄を削除してください。 |
403 origin_not_allowed が返る、またはすべての送信がスパムになる | ページのドメインがフォームの Allowed domains にありません。ドメインと、ローカルテスト用の localhost を追加してください。登録したドメインのサブドメインは許可されます。 |
| ブラウザーが自分のページではなく Ikiform のサンクスページに移動する | _redirect は、ドメインが Allowed domains にあり、https: の URL でないと無視されます。ドメインを追加するか、代わりにフォーム設定で Redirect after submit を設定してください。 |
よくある質問
はい。フォームはブラウザーから Ikiform などのフォームバックエンドに送信し、そこで各登録を保存してフィルタリングします。別の場所でホストしたリレーや自動化ツールがそれをメールツールに渡すので、静的サイト自体はサーバーのコードを実行しません。
やめてください。ページの JavaScript にあるキーはすべての訪問者に見え、メールツールのキーはたいていリスト全体を読んだり変更したりできます。キーはサイトの外で動くリレーに置いてください。
はい。フォームはプレーンな HTML で、Ikiform に送信します。GitHub Pages ではリレーを動かせないので、カスタムドメインの Cloudflare Worker でホストするか、Zapier や n8n などの自動化ツールを使ってください。
メールツールです。Ikiform は登録した本人にメールを送りません。リレーが読者を未確認として追加し、ツールが独自の確認リンクを送ります。
手順で詰まったときや、あなたの状況に合うガイドがほしいときはメールでどうぞ: hello@ikiform.com