Vous avez déployé un site sur GitHub Pages, Netlify ou Cloudflare Pages. Vous ajoutez un <form>, un visiteur clique sur Envoyer, et l’envoi n’aboutit nulle part : aucun serveur ne le reçoit de votre côté. Tout le problème est là : un formulaire de contact doit envoyer une requête POST quelque part, et un hébergement statique ne fournit pas ce point de réception.

Vous avez trois options réalistes : un mailto: un lien, une fonction serverless connectée à une API email ou un point de réception tiers. Examinons-les par ordre d’effort et voyons honnêtement leurs limites.

Option 1 : un lien mailto:

L’option sans effort consiste à supprimer le formulaire :

<a href="mailto:[email protected]">Email me</a>

Cela ne coûte rien mais échoue de trois façons :

  • Les collecteurs de spam lisent votre HTML. Une adresse en clair sur une page publique est collectée par les robots, et les spams commencent en quelques jours. (Nous avons écrit sur ce qui fonctionne contre la collecte séparément.)
  • Cela dépend du logiciel de messagerie du visiteur. mailto: ouvre l’application par défaut du système. Sur un ordinateur professionnel ou un navigateur mobile, c’est souvent un Outlook non configuré ou rien du tout. Le visiteur voit un onglet vide et abandonne.
  • Aucune structure. Aucun champ obligatoire, objet choisi ni format cohérent. <form action="mailto:..."> est encore pire : les navigateurs le gèrent différemment, voire pas du tout.

Verdict : convient à une page personnelle où perdre un message importe peu. Pas un formulaire de contact.

Option 2 : une fonction serverless + une API email

Solution maison : les fonctions serverless de l’hébergeur reçoivent le POST, puis une API email (Resend, Postmark, SES…) livre le message dans votre boîte. Le code est court :

// netlify/functions/contact.js (or a Vercel function, or a Worker)
export default async function handler(req) {
  const { name, email, message } = await req.json();
  if (!email || !message) {
    return new Response("Missing fields", { status: 400 });
  }

  // Every email API looks roughly like this
  await fetch("https://api.email-provider.example/send", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.EMAIL_API_KEY}` },
    body: JSON.stringify({
      to: "[email protected]",
      subject: `Contact form: ${name || "someone"}`,
      reply_to: email,
      text: message,
    }),
  });

  return Response.json({ ok: true });
}

Cela fonctionne, et reste raisonnable si vous utilisez déjà des fonctions. Mais comptez le coût réel de ce petit gestionnaire :

  • Une fonction à écrire, déployer et maintenir lorsque les environnements changent.
  • Un compte chez un fournisseur d’emails, généralement avec une vérification du domaine avant de fiabiliser la livraison.
  • Des clés API à créer, stocker comme variables d’environnement et renouveler.
  • L’antispam reste à votre charge. Le premier robot découvrant le point de réception l’utilisera : vous devrez alors écrire limites de débit et champs pièges.
  • Tout cela répété pour chaque site lancé. Le quatrième projet a encore besoin de sa fonction, de sa clé et de sa configuration.

Notez ce qui s’est passé : votre « site statique sans backend » en a désormais un. Petit, mais à votre charge.

Option 3 : un point de réception de formulaire

Un point de réception de formulaire est une URL qui accepte les requêtes POST du formulaire et place le message là où vous le verrez. Votre site reste entièrement statique ; le service fait office de backend. Toute l’intégration tient dans un attribut :

<form data-mailcontact-form action="https://mailcontact.app/api/forms/your-form-id" method="POST">
  <input type="text" name="name" placeholder="Your name" />
  <input type="email" name="email" placeholder="[email protected]" required />
  <input type="text" name="subject" placeholder="Subject" />
  <textarea name="message" placeholder="What's on your mind?" required></textarea>
  <button type="submit">Send</button>
  <input name="company_website" type="text" tabindex="-1" autocomplete="off"
    style="position:absolute;left:-10000px" aria-hidden="true" />
</form>

Voilà un formulaire fonctionnel. Avec le point de réception mailcontact, l’URL accepte les données de formulaire standard ou le JSON, exige un email et un message (le nom et l’objet sont facultatifs), et répond {"ok": true}. CORS est ouvert : cela fonctionne depuis n’importe quelle origine : GitHub Pages, Netlify, un bucket S3 et même un fichier HTML local. Les envois sont limités par adresse IP, et chaque message arrive dans la boîte de réception de votre projet.

Un conseil : pour garder les visiteurs sur la page après l’envoi, utilisez le script MailContact hébergé plutôt qu’un POST natif. Il gère confirmations, erreurs et vérification automatique, et injecte le contrôle caché si nécessaire :

<!-- Add data-mailcontact-form to the form above. -->
<script defer src="https://mailcontact.app/forms/v1.js"
  crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Quand est-ce adapté ? Quand le besoin est : « laisser les visiteurs me joindre sans maintenance ». Vous faites confiance à un service plutôt qu’à votre code. Pour une logique personnalisée, des fichiers ou un flux CRM, écrivez la fonction. Pour un formulaire statique, le point de réception gagne en temps, maintenance et nombre de clés API (zéro). Le guide des sites statiques présente les particularités de chaque hébergeur.

Recevoir des alertes et séparer les projets

Quelle que soit l’option, rappelez-vous l’objectif : lire réellement les messages. Un formulaire livré dans une boîte jamais consultée est un mailto: un lien avec des étapes en plus.

C’est ici que le modèle mailcontact aide. Chaque projet a une boîte, et un canal vous alerte à l’arrivée d’un message : Discord, Slack, par email ou par webhook. La même boîte dispose aussi d’une véritable adresse email ([email protected]) qui fonctionne immédiatement, sans DNS ni serveur mail : le formulaire et le lien « ou écrivez-nous par email » alimentent le même endroit.

Au projet suivant, vous créez une boîte plutôt que de faire pointer un formulaire de plus vers votre messagerie personnelle. Nous avons détaillé pourquoi chaque projet mérite sa boîte. En bref : séparer les échanges permet de gérer cinq projets au lieu de subir cinq sources de bruit.