Formulaire d'inscription à la newsletter6 min de lecture
Un formulaire d'inscription par e-mail sur un site statique, sans serveur
Ajoutez un formulaire d'inscription à la newsletter à un site statique Hugo, Jekyll, Eleventy ou Astro, gardez la clé d'API de l'outil d'e-mailing hors de la page et confirmez dans l'outil.
Sur un site statique, le formulaire envoie directement à Ikiform depuis le navigateur du lecteur, puisqu'il n'y a aucun serveur à vous vers lequel envoyer. Gardez la clé d'API de votre outil d'e-mailing totalement hors du site : le navigateur ne connaît que votre endpoint Ikiform public, et un relais hébergé ailleurs ajoute l'abonné. Écrivez le formulaire une fois sous forme de partial ou d'include, passez une source différente par emplacement, et ajoutez un petit script pour la version sans rechargement.
Ce qui change ici
Un site statique, ce sont des fichiers chez un hébergeur : GitHub Pages, Netlify, Cloudflare Pages, un bucket S3. Rien de ce que vous écrivez ne tourne sur un serveur quand un lecteur envoie un formulaire. Cela exclut le raccourci habituel qui consiste à appeler l'API de votre outil d'e-mailing depuis la page, car tout ce qu'exécute le navigateur, n'importe qui peut le lire.
Les clés d'API des outils d'e-mailing se limitent rarement à l'ajout d'un abonné. Une clé qui peut ajouter un abonné peut généralement aussi lire, exporter ou supprimer toute la liste. Mettez-la dans le JavaScript d'un site statique et elle part chez chaque visiteur, et de là chez quiconque ouvre les outils de développement.
- Avec la clé dans la page, chaque visiteur la télécharge, et n'importe qui peut appeler l'API de l'outil d'e-mailing avec.
- Avec un relais, la page ne connaît que votre endpoint Ikiform, qui accepte les envois de formulaire et rien d'autre.
- Ikiform filtre le spam et envoie un webhook signé au relais.
- Le relais, hébergé hors du site statique, détient la clé d'API et ajoute l'abonné.
Écrire le formulaire une fois
Un formulaire de newsletter apparaît dans plusieurs templates : le pied de page, la fin de chaque article, une page newsletter. Faites-en un partial qui accepte une valeur source, pour que chaque emplacement s'enregistre lui-même. Le markup est identique dans chaque générateur ; seule la syntaxe de template pour source change.
<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>Insérez-le ensuite dans un template avec le nom de l'emplacement :
{{ partial "newsletter.html" (dict "source" "post") }}- Dans Astro, utilisez
<Newsletter source="post" />. Un build Astro statique ne peut pas exécuter d'Actions : le composant envoie donc directement à Ikiform comme les autres. - Ajoutez
sourceà la définition des champs du formulaire dans Ikiform, en texte avec une longueur maximale courte. Une fois les champs définis, un input que la définition ne liste pas est refusé avecunknown_field. _redirectdoit être une URLhttps:sur l'un des Allowed domains du formulaire. Créez la page/subscribed/avec une ligne qui invite les lecteurs à vérifier leur boîte de réception.
Un seul script pour tous les formulaires de la page
Une page d'article peut avoir deux formulaires de newsletter, un après l'article et un dans le pied de page. Ce script gère chaque formulaire marqué data-newsletter, et quand l'un réussit, il les remplace tous, pour qu'on ne redemande à personne de s'abonner quelques centimètres plus bas. Chargez-le une fois depuis votre layout de base.
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;
});
}- Si
fetchéchoue, par exemple derrière un filtre réseau strict, le script se rabat surform.submit(), qui envoie de façon classique et arrive sur/subscribed/. - Un
429a son propre message. Les lecteurs d'un même réseau de bureau ou de campus partagent une IP et peuvent atteindre ensemble la limite par visiteur. - Le formulaire fonctionne avec JavaScript désactivé. Le script ne change que la façon d'afficher la réponse.
Les lecteurs sans JavaScript arrivent sur /subscribed/ : faites donc faire à cette page ce que fait le script pour tous les autres. Dites qu'un e-mail de confirmation arrive, nommez l'expéditeur à chercher, mentionnez le dossier spam et mettez un lien vers le blog. C'est une page ordinaire de votre générateur, construite comme les autres, et c'est aussi là que vous pouvez faire pointer ensuite le lien de confirmation de l'outil d'e-mailing.
Où vit le relais
Le relais a besoin d'une URL HTTPS publique qui exécute du code. Votre hébergeur statique en propose peut-être une, ou pas :
| Hébergeur statique | Options pour le relais |
|---|---|
| GitHub Pages, S3 | Pas de fonctions. Utilisez un Cloudflare Worker sur un domaine personnalisé, ou un outil d'automatisation |
| Netlify | Une Netlify Function sur le domaine de votre site, ou l'une des options ci-dessus |
| Cloudflare Pages | Un Worker séparé sur un domaine personnalisé. Ikiform refuse les URL pages.dev et workers.dev |
| Vercel | Une fonction sur votre domaine. La sortie statique peut cohabiter avec une route API |
Le code du relais pour chaque outil d'e-mailing est sur les pages Kit, MailerLite et beehiiv. Sans aucun code, faites pointer le webhook vers Zapier ou n8n et utilisez leur action pour l'outil d'e-mailing. La documentation des webhooks liste les règles d'URL.
localhost.Si votre site envoie une Content Security Policy
Beaucoup d'hébergeurs statiques permettent de définir des en-têtes depuis un fichier, comme _headers sur Netlify et Cloudflare Pages. Si le vôtre envoie une Content-Security-Policy, le navigateur bloque le formulaire sauf si la politique autorise Ikiform. form-action couvre l'envoi classique et connect-src le fetch du script :
Content-Security-Policy: default-src 'self'; form-action 'self' https://ikiform.com; connect-src 'self' https://ikiform.comUn envoi bloqué apparaît comme une erreur CSP dans la console du navigateur et n'atteint jamais Ikiform : rien n'apparaît donc non plus dans le tableau de bord.
Vérifiez que tout fonctionne
- Ajoutez votre domaine de production et
localhostsous Allowed domains. - Buildez le site et ouvrez une page d'article. Les deux formulaires sont là, chacun avec sa propre valeur
sourcedans le code source de la page. - Abonnez-vous depuis le formulaire de l'article avec une adresse à vous. Les deux formulaires affichent la ligne vous invitant à vérifier votre boîte.
- Désactivez JavaScript et abonnez-vous depuis le pied de page. Vous arrivez sur
/subscribed/. - Ouvrez Submissions dans le tableau de bord. Les deux inscriptions affichent
postetfootercommesource. - Cherchez la clé d'API de votre outil d'e-mailing dans le site buildé. Elle n'y est pas.
Pièges
| Problème | Que faire |
|---|---|
| Rien dans le tableau de bord, et une erreur CSP dans la console | Ajoutez https://ikiform.com à form-action et connect-src dans votre Content-Security-Policy. |
422 avec unknown_field pour source | Ajoutez source à la définition des champs du formulaire, ou supprimez l'input masqué. |
403 origin_not_allowed, ou tous les envois finissent en spam | Le domaine de la page n'est pas dans les Allowed domains du formulaire. Ajoutez-le, plus localhost pour les tests en local. Les sous-domaines d'un domaine listé sont autorisés. |
| Le navigateur arrive sur la page de remerciement d'Ikiform au lieu de la vôtre | _redirect est ignoré sauf si son domaine figure dans Allowed domains et qu'il s'agit d'une URL https:. Ajoutez le domaine, ou définissez plutôt Redirect after submit dans les réglages du formulaire. |
Questions
Oui. Le formulaire envoie depuis le navigateur à un backend de formulaires comme Ikiform, qui stocke et filtre chaque inscription. Un relais ou un outil d'automatisation hébergé ailleurs la transmet à votre outil d'e-mailing : le site statique lui-même n'exécute aucun code serveur.
Non. Toute clé dans le JavaScript de la page est visible par chaque visiteur, et les clés des outils d'e-mailing peuvent généralement lire ou modifier toute la liste. Gardez la clé dans un relais qui tourne hors du site.
Oui. Le formulaire est du HTML simple et envoie à Ikiform. GitHub Pages ne peut pas exécuter le relais : hébergez-le sur un Cloudflare Worker avec un domaine personnalisé, ou utilisez un outil d'automatisation comme Zapier ou n8n.
Votre outil d'e-mailing. Ikiform n'écrit jamais à la personne qui s'inscrit. Le relais ajoute le lecteur comme non confirmé et l'outil envoie son propre lien de confirmation.
Une étape vous bloque, ou vous voulez un guide pour votre cas ? Écrivez à hello@ikiform.com.