Hai pubblicato un sito su GitHub Pages, Netlify o Cloudflare Pages. Aggiungi un <form>, un visitatore incontra Invia, e l’invio non arriva da nessuna parte perché non hai un server che lo riceva. Il problema è tutto qui: un modulo di contatto ha bisogno di una destinazione per il POST e un hosting statico non la fornisce.
Hai tre opzioni realistiche: un mailto: link, una funzione serverless collegata a un’API email o un endpoint di terze parti. Esaminiamoli in ordine di impegno e vediamo con onestà i limiti di ciascuno.
Opzione 1: un link mailto:
L’opzione più semplice è rinunciare al modulo:
<a href="mailto:[email protected]">Email me</a>Non costa nulla ma fallisce in tre modi:
- I raccoglitori di spam leggono il tuo HTML. Un indirizzo in testo semplice su una pagina pubblica viene raccolto dai bot e lo spam inizia in pochi giorni. (Abbiamo scritto di cosa funziona davvero contro la raccolta automatica separatamente.)
- Dipende dal client email del visitatore.
mailto:apre l’app predefinita del sistema. Su un laptop di lavoro o browser mobile è spesso Outlook non configurato, o nulla. Il visitatore guarda una scheda vuota e rinuncia. - Non hai alcuna struttura. Nessun campo obbligatorio, nessun oggetto scelto, nessun formato coerente.
<form action="mailto:...">è ancora peggio: i browser lo gestiscono in modi diversi e alcuni non lo gestiscono affatto.
Verdetto: adatto a una pagina personale se perdere messaggi non importa. Non è un modulo di contatto.
Opzione 2: una funzione serverless + un’API email
La via fai da te: le funzioni serverless dell’hosting ricevono il POST e un’API email (Resend, Postmark, SES, a scelta) lo consegna alla casella. Il codice è davvero breve:
// 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 });
}Funziona e, se usi già funzioni, è ragionevole. Ma considera il costo reale di quel piccolo gestore:
- Una funzione da scrivere, pubblicare e mantenere quando cambiano i runtime.
- Un account con un fornitore email, di solito con verifica del dominio per una consegna affidabile.
- Chiavi API da creare, salvare come variabili d’ambiente e ruotare.
- L’antispam è un problema tuo. Il primo bot che scopre l’endpoint lo userà, e ti ritrovi a scrivere limiti di frequenza e honeypot.
- Tutto duplicato per ogni sito che pubblichi. Anche il quarto progetto richiede la propria funzione, chiave e configurazione.
E guarda cosa è successo: il tuo “sito statico senza backend” ora ha un backend. Piccolo, ma sei tu a gestirlo.
Opzione 3: un endpoint per moduli
Un endpoint per moduli è un URL che accetta il POST del modulo e colloca il messaggio dove lo vedrai davvero. Il sito resta interamente statico; il servizio funge da backend. Tutta l’integrazione è un attributo:
<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>Ecco un modulo di contatto funzionante. Con l’endpoint dei moduli di mailcontact, l’URL accetta dati standard dei moduli o JSON e richiede un valido email e un message (nome e oggetto sono facoltativi), e risponde {"ok": true}. CORS è aperto, quindi funziona da qualsiasi origine: GitHub Pages, Netlify, un bucket S3, persino un file HTML locale. Gli invii sono limitati per IP e ogni messaggio arriva nella casella del progetto.
Nota pratica: per mantenere i visitatori sulla pagina dopo l’invio, usa lo script ospitato MailContact invece dell’invio nativo. Gestisce conferme, errori e verifica automatica e inserisce il controllo nascosto quando serve:
<!-- Add data-mailcontact-form to the form above. -->
<script defer src="https://mailcontact.app/forms/v1.js"
crossorigin="anonymous" referrerpolicy="no-referrer"></script>Quando conviene? Quando vuoi che i visitatori ti contattino senza mantenere nulla. Ti affidi a un servizio anziché al tuo codice: è l’accordo. Se servono logica personalizzata, allegati o un flusso CRM, scrivi la funzione. Per un modulo su sito statico, l’endpoint vince su tempi di configurazione, manutenzione e numero di chiavi API (zero). La guida ai siti statici tratta i dettagli specifici dell’hosting.
Ricevere notifiche e tenere separati i progetti
Qualunque opzione scegli, ricorda perché volevi il modulo: leggere davvero i messaggi. Un modulo che consegna a una casella che non controlli è un mailto: link con passaggi aggiuntivi.
Qui aiuta il modello mailcontact. Ogni progetto ha una casella e un canale ti avvisa quando arriva qualcosa: Discord, Slack, email o webhook. La stessa casella include anche un vero indirizzo email ([email protected]) che funziona subito, senza DNS né server di posta, così il modulo e il link “oppure scrivici un’email” arrivano nello stesso posto.
E quando lanci il progetto successivo, crei una nuova casella invece di puntare un altro modulo alla tua posta personale. Abbiamo descritto perché ogni progetto personale merita la sua casella. In breve: separare le conversazioni rende gestibili cinque progetti senza moltiplicare il rumore.