Du hast eine Website auf GitHub Pages, Netlify oder Cloudflare Pages bereitgestellt und ergänzt ein <form>, ein Besucher ruft Senden, und die Nachricht kommt nirgendwo an, weil auf deiner Seite kein Server sie empfängt. Genau das ist das Problem: Ein Kontaktformular braucht ein POST-Ziel, das ein statischer Host nicht bereitstellt.

Du hast drei realistische Möglichkeiten: ein mailto: Link, eine Serverless-Funktion mit E-Mail-API oder einen externen Formular-Endpunkt. Gehen wir sie nach Aufwand durch und schauen ehrlich auf ihre Grenzen.

Option 1: ein mailto:-Link

Die aufwandslose Option lässt das Formular ganz weg:

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

Es kostet nichts und scheitert auf drei Arten:

  • Spam-Sammler lesen dein HTML. Eine öffentlich sichtbare Klartextadresse wird von Bots gesammelt. Innerhalb weniger Tage beginnt der Spam. Wir haben darüber geschrieben, was wirklich gegen das Auslesen hilft gesondert.)
  • Es hängt vom E-Mail-Programm des Besuchers ab. mailto: öffnet die Systemstandard-App. Auf Arbeitslaptops oder Mobilbrowsern ist das oft ein nicht eingerichtetes Outlook oder gar nichts. Der Besucher sieht einen leeren Tab und gibt auf.
  • Es fehlt die Struktur. Keine Pflichtfelder, kein selbst gewählter Betreff, kein einheitliches Format. <form action="mailto:..."> ist noch schlechter: Browser behandeln es uneinheitlich, manche gar nicht.

Fazit: Für eine private Homepage okay, wenn verlorene Nachrichten egal sind. Kein Kontaktformular.

Option 2: Serverless-Funktion + E-Mail-API

Der Eigenbau: Serverless-Funktionen deines Hosts empfangen POST, eine E-Mail-API wie Resend, Postmark oder SES liefert ins Postfach. Der Code ist tatsächlich kurz:

// 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 });
}

Das funktioniert und ist vernünftig, wenn du ohnehin Funktionen betreibst. Rechne aber den tatsächlichen Aufwand des kleinen Handlers:

  • Eine Funktion schreiben, bereitstellen und bei Laufzeitänderungen weiter pflegen.
  • Ein Konto bei einem E-Mail-Anbieter, meist mit Domainprüfung, bevor die Zustellung zuverlässig klappt.
  • API-Schlüssel erstellen, als Umgebungsvariablen speichern und regelmäßig erneuern.
  • Spamschutz bleibt deine Aufgabe. Der erste Bot nutzt den gefundenen Endpunkt, und schon schreibst du Ratenlimits und Honeypots.
  • Alles erneut für jede veröffentlichte Website. Auch Projekt Nummer vier braucht eine eigene Funktion, einen Schlüssel und Konfiguration.

Beachte, was dabei passiert ist: Deine „statische Website ohne Backend“ hat jetzt doch ein Backend. Ein kleines, aber du musst es pflegen.

Option 3: Formular-Endpunkt

Ein Formular-Endpunkt ist eine URL, die den POST deines Formulars annimmt und die Nachricht dort ablegt, wo du sie siehst. Deine Website bleibt statisch; der Dienst übernimmt das Backend. Die gesamte Einbindung besteht aus einem 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>

Das ist ein funktionierendes Kontaktformular. Mit Mailcontact-Formular-Endpunkt, akzeptiert die URL Standard-Formulardaten oder JSON und erfordert ein gültiges email und ein message (Name und Betreff sind optional) und antwortet mit {"ok": true}. CORS ist offen, daher funktioniert es von jeder Herkunft aus: GitHub Pages, Netlify, einem S3-Bucket oder sogar einer lokalen HTML-Datei. Einsendungen werden pro IP begrenzt und landen im Postfach deines Projekts.

Ein praktischer Hinweis: Damit Besucher nach dem Senden auf der Seite bleiben, nutze das gehostete MailContact-Skript statt eines nativen POST. Es übernimmt Bestätigung, Fehler und automatische Verifizierung und fügt bei Bedarf das verborgene Prüffeld ein:

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

Wann passt das? Wenn Besucher dich erreichen sollen, ohne dass du etwas pflegen musst. Du vertraust einem Dienst statt deinem Code. Für eigene Logik, Anhänge oder CRM-Pipelines schreib eine Funktion. Beim statischen Kontaktformular gewinnt der Endpunkt bei Einrichtung, Pflege und API-Schlüsseln: null. Die Anleitung für statische Websites erklärt hostspezifische Details.

Benachrichtigungen und getrennte Projekte

Egal, welche Option du wählst: Das Formular soll dazu führen, dass du Nachrichten wirklich liest. Liefert es in ein nie geprüftes Postfach, ist es ein mailto: Link mit Zusatzschritten.

Hier hilft das Mailcontact-Modell. Jedes Projekt hat ein Postfach und ein Kanal meldet neue Nachrichten: Discord, Slack, E-Mail oder Webhook. Dasselbe Postfach bietet auch eine echte E-Mail-Adresse ([email protected]), die sofort funktioniert, ohne DNS oder Mailserver. So führen Formular und „oder schreib uns eine E-Mail“ an denselben Ort.

Beim nächsten Projekt erstellst du ein neues Postfach, statt noch ein Formular an dein privates Postfach zu hängen. Wir haben beschrieben, warum jedes Nebenprojekt sein eigenes Postfach verdient. Kurz gesagt: Durch Trennung bleiben fünf Projekte überschaubar, statt zu fünf Lärmquellen zu werden.