← Alle Guides
Hugo

Kontaktformular für Hugo – ganz ohne Server

Ein Partial, ein Shortcode, fertig: So bekommt jede Hugo-Seite ein Kontaktformular. Daten in der EU (Frankfurt) gespeichert, Spamschutz ohne Captcha.

Hugo erzeugt statische Dateien. Auf deinem Host gibt es deshalb nichts, was ein abgeschicktes Formular entgegennehmen könnte. Einen Server brauchst du dafür trotzdem nicht: Ein normales HTML-Formular schickt die Daten an Formlane, und per Shortcode baust du es in jede Markdown-Seite ein.

So geht's

  1. Melde dich bei Formlane an, leg ein Formular an und kopiere den Access Key (fl_…).
  2. Trag den Key in hugo.toml unter [params] als formlane_key ein.
  3. Leg das Partial und den Shortcode unten an und setz {{< contact-form >}} auf deine Kontaktseite.
  4. Leg content/thanks.md an, bau die Seite mit hugo, veröffentliche sie und schick eine Testnachricht.

Code

hugo.toml
[params]
  formlane_key = "fl_YOUR_ACCESS_KEY"

Der Key ist absichtlich öffentlich: Mit ihm kann man nur Einsendungen an genau dieses Formular schicken. Zur Sicherheit beschränkst du das Formular in den Einstellungen auf deine Domain.

layouts/partials/contact-form.html
<form action="https://smallwerk.com/formlane/api/submit" method="POST" accept-charset="UTF-8">
  <input type="hidden" name="access_key" value="{{ site.Params.formlane_key }}">
  <input type="hidden" name="subject" value="New message from {{ site.Title }}">
  <input type="hidden" name="redirect" value="{{ "thanks/" | absURL }}">
  <!-- Honeypot: hidden from people, leave it unchecked -->
  <input type="checkbox" name="botcheck" style="display:none" tabindex="-1">

  <label for="cf-name">Name</label>
  <input id="cf-name" type="text" name="name" autocomplete="name" required>

  <label for="cf-email">Email</label>
  <input id="cf-email" type="email" name="email" autocomplete="email" required>

  <label for="cf-message">Message</label>
  <textarea id="cf-message" name="message" rows="5" required></textarea>

  <button type="submit">Send</button>
</form>
layouts/shortcodes/contact-form.html
{{ partial "contact-form.html" . }}
content/contact.md
---
title: "Kontakt"
---

Fragen? Schreib uns.

{{< contact-form >}}
content/thanks.md
---
title: "Danke"
---

Danke für deine Nachricht. Wir melden uns bald.

Die Beschriftungen im Partial (Name, Email, Message, Send) sind englisch, wie in allen Formlane-Snippets. Für eine deutsche Seite ersetzt du sie einfach durch Name, E-Mail, Nachricht und Senden; die name-Attribute bleiben, wie sie sind.

Die Danke-Seite

Nach dem Absenden schickt das Feld redirect deine Besucher auf /thanks/. Die Adresse muss eine absolute http(s)-URL sein; absURL baut sie aus der baseURL in deiner hugo.toml. Lässt du redirect weg, zeigt Formlane seine eigene Danke-Seite.

Optional: auf der Seite bleiben

Willst du ohne Seitenwechsel eine Bestätigung zeigen, schickst du das Formular per fetch(). Mit Accept: application/json antwortet Formlane mit {"success": true, "message": "Form submitted successfully", "id": "…"} oder mit {"success": false, "error": "limit_reached", "message": "…"} und einem 4xx/5xx-Status. message ist immer englisch, deshalb zeigt das Snippet unten je nach error-Code einen eigenen deutschen Text.

layouts/partials/contact-form.html (append)
<script>
  const MESSAGES = {
    limit_reached: "Dieses Formular nimmt diesen Monat keine Nachrichten mehr an. Bitte schreib uns direkt per E-Mail.",
    rate_limited: "Zu viele Nachrichten in kurzer Zeit. Bitte versuch es in ein paar Minuten noch einmal.",
  };
  const form = document.querySelector('form[action*="formlane"]');
  if (form) form.addEventListener("submit", async (e) => {
    e.preventDefault();
    const res = await fetch(form.action, {
      method: "POST",
      headers: { Accept: "application/json" },
      body: new FormData(form),
    });
    const json = await res.json().catch(() => ({}));
    // Auch Spam (Honeypot oder Inhaltsregeln) bekommt success: true.
    form.outerHTML = json.success
      ? "<p>Danke! Wir melden uns bald.</p>"
      : "<p>" + (MESSAGES[json.error] || "Das hat leider nicht geklappt. Bitte versuch es später noch einmal.") + "</p>";
  });
</script>

Spamschutz ohne Captcha

  • Honeypot: die versteckte Checkbox botcheck. Kreuzt ein Bot sie an, bekommt er eine normale Erfolgsmeldung, gespeichert wird aber nichts.
  • Rate-Limit: höchstens 10 Einsendungen in 10 Minuten pro Besucher und Formular.
  • Inhaltsregeln: Nachrichten mit 4 oder mehr Links, Link-Markup oder typischen Spam-Phrasen landen in einem eigenen Spam-Tab. Spam zählt nicht zu deinem Monatskontingent.
  • Erlaubte Domains: In den Formular-Einstellungen beschränkst du das Formular auf deine eigene Domain.

Wo die Daten liegen

Die Einsendungen werden in der EU (Frankfurt) gespeichert, in einer Supabase-Datenbank in der Region eu-central-1. Die App läuft bei Vercel mit Rechenzentrum in Frankfurt; statische Dateien und das Edge-Netz sind global. Alle Dienstleister stehen in der Datenschutzerklärung. Betreiber ist die Mindcaptain Services OÜ in Tallinn, Estland. Ein Auftragsverarbeitungsvertrag ist in den AGB enthalten (Art. 28 DSGVO, smallwerk.com/avv), und IP-Hashes werden nach 30 Tagen gelöscht. Nimm das Kontaktformular in deine eigene Datenschutzerklärung auf und lösch alte Einsendungen im Dashboard, wenn du sie nicht mehr brauchst.

Preise

Free: 250 Einsendungen im Monat und bis zu 5 Formulare. Starter: 8 $ im Monat für 2.500 Einsendungen. Pro: 19 $ im Monat für 25.000 Einsendungen.

Häufige Fragen

Funktioniert das auf GitHub Pages, Netlify und Cloudflare Pages?

Ja. Diese Hosts liefern nur deine statischen Dateien aus; das Formular schickt seine Daten direkt an Formlane.

Muss ich in Goldmark unsicheres HTML erlauben?

Nein. Das Formular steckt in einem Partial und kommt per Shortcode in dein Markdown. markup.goldmark.renderer.unsafe bleibt also aus.

Mein Theme hat schon ein Formular für Formspree oder Netlify. Kann ich das Markup behalten?

Meist ja. Setz die Action auf https://smallwerk.com/formlane/api/submit und ergänze das versteckte Feld access_key. Die Feldnamen email und subject sowie der Honeypot _gotcha funktionieren unverändert. Netlify-Attribute wie data-netlify oder netlify-honeypot kannst du entfernen.

Bekomme ich für jede Nachricht eine E-Mail?

Ja. Jede Nachricht, die nicht als Spam markiert ist, kommt per E-Mail zu dir, mit Reply-To auf die Adresse des Absenders. Außerdem findest du sie in deinem Formlane-Dashboard und kannst sie als CSV exportieren.

Können Besucher Dateien hochladen?

Noch nicht. Formlane nimmt nur Textfelder an.

Was passiert, wenn das Monatslimit erreicht ist?

Neue Einsendungen werden mit dem Fehler limit_reached abgelehnt, bis der nächste Kalendermonat beginnt oder du upgradest. Was schon da ist, bleibt erhalten.

Access Key holen

Kostenlos für 250 Einsendungen pro Monat. Daten in der EU (Frankfurt) gespeichert.

Access Key holen