Formular einbinden
So bindet man ein Formular einer statischen Seite an das Form Gateway an —
ohne eigenes Backend, ohne Build-Schritt, mit einer einzigen <script>-Zeile.
Das Snippet (client/form-gateway.js) ist dependency-freies Vanilla-JS (~1,6 KB gzip, minifiziert ausgeliefert) und übernimmt den kompletten sicheren Ablauf: Challenge holen → Proof-of-Work im Web Worker lösen → Formulardaten absenden → Erfolg/Fehler inline anzeigen. Die Seite braucht selbst kein Geheimnis — der Site-Key ist eine öffentliche Kennung (siehe Konzept §2).
Zum Ausprobieren: examples/astro-demo ist eine lauffähige statische Astro-Seite, die den Einbau und alle Feld-Möglichkeiten mit Live-Formularen zeigt.
1. Voraussetzung: Mandant anlegen
Abschnitt betitelt „1. Voraussetzung: Mandant anlegen“Im Admin-UI des Gateways (Menü Mandanten → Neuer Mandant):
- Empfänger-Adresse — wohin die Formularmails gehen (kommt nie aus dem Formular).
- Erlaubte Origins — die Domains, von denen Formulare kommen dürfen,
eine pro Zeile, mit Schema und ohne Pfad, z. B.
https://www.example.de. Für lokale Tests auchhttp://127.0.0.1:8099o. ä. eintragen. - optional PoW-Bits, Spam-Schwelle, Tageskontingent.
Nach dem Speichern zeigt die Liste den Site-Key (site_…). Den
braucht das Formular. Außerdem muss mindestens eine SMTP-Route
existieren (Menü SMTP-Routen) — üblicherweise die geteilte
Default-Route über euren Mailserver.
2. Minimaler Einbau
Abschnitt betitelt „2. Minimaler Einbau“<form data-gateway-site="site_DEIN_SITE_KEY" data-gateway-url="https://forms.example.de"> <input name="name" required> <input name="_gw_reply" type="email" placeholder="Ihre E-Mail"> <textarea name="nachricht" required></textarea> <button type="submit">Senden</button></form>
<script src="https://forms.example.de/form-gateway.js" defer></script>Das war alles. data-gateway-url ist die Basis-URL eurer
Gateway-Installation; das Snippet wird von dort geladen und spricht
dieselbe Adresse an. Jedes <form> mit data-gateway-site auf der
Seite wird automatisch verdrahtet — auch mehrere.
Ein vollständiges Beispiel liegt unter client/example.html.
3. Attribute am <form>
Abschnitt betitelt „3. Attribute am <form>“| Attribut | Pflicht | Bedeutung |
|---|---|---|
data-gateway-site | ja | Der Site-Key des Mandanten (site_…) |
data-gateway-url | ja | Basis-URL der Gateway-Installation (ohne Pfad) |
data-gateway-form | nein | Formular-Kennung, landet als Betreff-Zusatz und im Admin (z. B. kontakt, bewerbung) |
data-gateway-sending | nein | Text während des Sendens (Default: „Wird gesendet …”) |
data-gateway-success | nein | Erfolgsmeldung (Default: „Vielen Dank! Ihre Nachricht wurde gesendet.”) |
data-gateway-error | nein | Fehlermeldung (Default: „Senden fehlgeschlagen …“) |
4. Feldnamen
Abschnitt betitelt „4. Feldnamen“Es gibt kein Schema im Gateway: jedes Feld mit name wird übertragen
und landet im Mail-Text — ein neues Feld braucht nur HTML.
Feldnamen dürfen nur a-z, A-Z, 0-9, _ und - enthalten (max. 64
Zeichen). Ein ungültiger Name (Umlaut, Leerzeichen) lässt die komplette
Absendung scheitern, er wird nicht übersprungen — Umlaute gehören ins
<label>, nicht in den Feldnamen.
Mehrfachwerte: Felder mit gleichem name (Checkbox-Gruppen,
<select multiple>) werden mit , zusammengefasst → web, hosting.
Eine nicht angehakte Checkbox fehlt komplett (normales HTML-Verhalten).
<input type="file"> wird ignoriert — das Gateway versendet keine Anhänge.
Im Mail-Text stehen die Felder alphabetisch nach Feldnamen sortiert,
nicht in Formular-Reihenfolge. Wer eine feste Reihenfolge braucht, stellt
eine Zahl voran (01_name, 02_firma).
Alle name-Felder außer den reservierten landen im Mail-Text. Reserviert:
| Feldname | Zweck |
|---|---|
_gw_reply | Antwortadresse des Einsenders → wird Reply-To der Mail (validiert). Optional, aber empfohlen. |
_gw_form | Alternative zu data-gateway-form, falls die Kennung aus dem Formular kommen soll. |
_gw_hp | Honeypot — fügt das Snippet selbst unsichtbar ein, nicht von Hand anlegen. |
Grenzen der Eingabe-Härtung: max. 30 Felder, 5.000 Zeichen pro Feld, 20.000 Zeichen gesamt; Zeilenumbrüche in Kopf-Feldern und ungültige Reply-To-Adressen werden abgewiesen.
5. Rückmeldung gestalten
Abschnitt betitelt „5. Rückmeldung gestalten“Das Snippet hängt unter das Formular ein <p class="gw-status"> und
setzt darauf ein data-state-Attribut (sending | success | error).
Damit lässt sich der Status frei stylen:
.gw-status { margin-top: 1rem; font-weight: 600; }.gw-status[data-state="success"] { color: #087443; }.gw-status[data-state="error"] { color: #b3261e; }.gw-status[data-state="sending"] { color: #555; }Die Meldungstexte selbst kommen aus den data-gateway-*-Attributen
(§3) — so bleibt die Sprache in eurer Hand.
6. Progressive Enhancement
Abschnitt betitelt „6. Progressive Enhancement“Ohne JavaScript bleibt das <form> ein normales Formular. Wer einen
Fallback ohne JS will, setzt am <form> z. B. ein
action="mailto:kontakt@example.de" — läuft JS, fängt das Snippet das
Submit ab (preventDefault) und nutzt das Gateway; ohne JS greift der
mailto-Fallback des Browsers.
Die Challenge wird beim ersten Fokus ins Formular vorab geholt (das startet zugleich die Mindest-Ausfüllzeit-Uhr) — der Absende-Klick fühlt sich dadurch schneller an.
7. Snippet selbst hosten (optional)
Abschnitt betitelt „7. Snippet selbst hosten (optional)“Standard ist, das Snippet direkt vom Gateway zu laden
(<script src="https://forms.example.de/form-gateway.js">) — so bekommen
alle Seiten automatisch dieselbe, aktuelle Version. Wer es lieber selbst
ausliefert (eigenes CDN, striktere CSP), lädt einmalig die Datei von
…/form-gateway.js (minifiziert) bzw. …/form-gateway.src.js (lesbar)
und legt sie neben die eigenen Assets. Funktional identisch — aber dann
liegt es an einem selbst, Snippet-Updates nachzuziehen.
Caching: Das Gateway liefert das Snippet mit
Cache-Control: public, max-age=300, must-revalidate und einem ETag aus.
Kundenseiten haben einen Fix also nach spätestens fünf Minuten; die
Revalidierung kostet nur ein 304 ohne Body. Bewusst kurz gewählt: das
Snippet ist sicherheitsrelevant (PoW, Feld-Einsammlung) und hat keine
Version in der URL — ein langes max-age wäre eine Falle.
CSP-Hinweis: Das Snippet startet den Proof-of-Work in einem Web
Worker aus einem blob:-URL. Bei strenger Content-Security-Policy
müssen script-src (bzw. worker-src) blob: erlauben, und
connect-src muss die Gateway-URL zulassen. Ohne Worker fällt das
Snippet automatisch auf Lösen im Hauptthread zurück.
8. Fehlersuche
Abschnitt betitelt „8. Fehlersuche“| Symptom | Ursache / Lösung |
|---|---|
| Netzwerk-Fehler beim Challenge-Abruf, Formular meldet sofort Fehler | Origin nicht in der Allowlist des Mandanten. Exakt mit Schema und ohne Pfad eintragen (https://www.example.de). Der Browser-Origin-Header muss passen. |
| „Senden fehlgeschlagen” trotz korrektem Origin | Mandant deaktiviert (Kill-Switch), Tageskontingent erschöpft oder Rate-Limit — der genaue Grund steht im Admin/Log (die Public API antwortet bewusst wortkarg). |
| Absenden dauert spürbar lange | PoW-Bits zu hoch. Der Browser hasht via WebCrypto einzeln; 12 Bits ≈ 1 s, 18 Bits ≈ 40 s. Im Mandanten die PoW-Bits senken (Empfehlung 8–14). |
| Nachricht kommt nicht an, im Admin steht „failed: keine smtp-route” | Es gibt keine (Default-)SMTP-Route. Im Admin unter SMTP-Routen anlegen und mit dem Test-Versand-Button prüfen. |
| Mail landet in Quarantäne | Spam-Score über der Schwelle. Im Mandanten-Detail freigeben oder verwerfen; ggf. Schwelle anpassen. |
Für tiefere Analyse …/form-gateway.src.js laden (unminifiziert) und die
Browser-Devtools (Netzwerk-Tab: challenge, submit) nutzen.
9. Was das Snippet NICHT tut
Abschnitt betitelt „9. Was das Snippet NICHT tut“- Es speichert kein Geheimnis — der Site-Key ist öffentlich; die Sicherheit liegt in der serverseitigen Prüf-Pipeline (architecture.md, „Der Submit-Pfad”).
- Es setzt keine Cookies und trackt nichts.
- Es lädt keine Drittressourcen — alles läuft gegen eure Gateway-Installation.