Wer ein Kontaktformular auf seiner Webseite anbietet, kennt das: Zwischen den echten Anfragen landen Angebote für Backlinks, Krypto-Tipps und Nachrichten in Sprachen, die im Team niemand spricht. Jede davon wird übertragen, gespeichert, gefiltert und am Ende von einem Menschen gelesen und gelöscht. Spam kostet Zeit und, im Kleinen, auch Strom und Speicherplatz.
Wir haben einiges ausprobiert, bevor wir bei einer Lösung gelandet sind, die heute eher ungewöhnlich ist: einem Formular, das selbst gar nichts abschickt, sondern das E-Mail-Programm öffnet.
Ein typisches Kontaktformular
So oder so ähnlich sieht ein Kontaktformular auf den meisten Webseiten aus:
<form method="POST" action="...">
<label for="name">Name</label>
<input id="name" name="name" type="text" />
<label for="email">E-Mail</label>
<input id="email" name="email" type="email" />
<label for="message">Nachricht</label>
<textarea id="message" name="message" rows="10" cols="30"></textarea>
</form>Natürlich möchtest du wissen, wer dir schreibt. Für die erste Nachricht ist der Name aber zweitrangig, das Feld ist also optional.
Ohne E-Mail-Adresse kannst du nicht antworten, sie ist also Pflicht. Ob sie zu einem echten Menschen gehört, weißt du an dieser Stelle nicht. Du kannst prüfen, ob die Adresse gültig aussieht, aber nicht, ob jemand dahintersitzt. Genau dieses Feld füllen Bots am liebsten aus.
Die Nachricht ist der eigentliche Grund für das Formular, und hier beginnt das Problem. Was jemand schreibt, kannst und solltest du nicht vorgeben. Damit ist es auch das Feld, in dem der Spam steht.
Was gegen Spam üblich ist und was es kostet
Spam lässt sich nicht zu hundert Prozent erkennen. Es gibt aber ein paar gängige Wege, die Menge zu verringern. Jeder davon hat einen Preis, der nicht immer auf der Rechnung steht.
Inhalte filtern
Naheliegend ist, die Eingaben auf verdächtige Inhalte zu prüfen, etwa auf eine ungültige E-Mail-Adresse oder auf Wörter wie Bitcoin. Das funktioniert eine Weile. Dann schreibt der Absender eben BTC oder Crypto, und die Liste der „bösen“ Wörter wächst. Jemand muss sie pflegen, und die Prüfung braucht ein Skript auf dem Server, das bei jeder Anfrage läuft.
Captchas und Bibliotheken von Drittanbietern
Ein Captcha soll beweisen, dass ein Mensch das Formular abschickt und kein Bot. Dafür bindest du Skripte oder eine API eines Drittanbieters ein. Bei manchen Diensten wird das Skript auf jeder Seite geladen, nicht nur beim Formular, damit es das Verhalten der Besucher bewerten kann. Jeder Seitenaufruf überträgt dann mehr Daten, und ein Teil davon landet beim Anbieter, was datenschutzrechtlich mindestens heikel ist.
Der größte Nachteil trifft aber die Menschen, für die das Formular gedacht ist: Sie sollen Rätsel lösen oder Ampeln und Zebrastreifen anklicken, bevor sie dir schreiben dürfen. Aus Sicht der Benutzerfreundlichkeit ist das eine Lösung, die du deinen Besuchern nicht zumuten solltest.
Formulardienste (Low-Code / No-Code)
Bequem sind Formulardienste: Das action-Attribut des Formulars zeigt auf die URL eines Form-as-a-Service-Anbieters, der Spam herausfiltert und die Nachricht per E-Mail oder Push-Benachrichtigung weiterleitet. Damit liegen aber Name, Adresse und Inhalt jeder Anfrage bei einem weiteren Unternehmen. Das heißt ein weiterer Vertrag, ein weiterer Absatz in der Datenschutzerklärung, oft ein weiteres Abo, und jede Nachricht nimmt einen Umweg über fremde Server.
Was wir stattdessen wollten
Nachdem wir all das ausprobiert und etliche Bibliotheken eingebaut hatten, die versprachen, Spam zu verhindern, haben wir das Kontaktformular grundsätzlich hinterfragt. Die Lösung sollte die Codebasis nicht aufblähen, ohne Skripte von Drittanbietern auskommen und Besucher nicht mit Rätseln aufhalten.
Heute kommt für uns noch etwas dazu: Unsere Seite ist eine statische Webseite, gebaut mit Hugo. Es gibt kein PHP und keine Datenbank, die ein Formular entgegennehmen könnten. Ein klassisches Kontaktformular würde genau diese Technik zurückbringen, oder eben einen Formulardienst.
Die Lösung: ein mailto-Link
Jeder kennt die „Kontakt“-Links, bei denen sich nach dem Klick das E-Mail-Programm öffnet. Früher waren sie unbeliebt, weil Besucher eine Kontaktseite oder ein Formular erwartet hatten und plötzlich in ihrem Mailprogramm landeten. Wir kombinieren deshalb beides: ein Formular auf der Seite, dessen Inhalt am Ende über einen mailto-Link verschickt wird.
Die Felder für Name und E-Mail fallen dabei weg. Die Nachricht geht aus dem E-Mail-Programm des Besuchers raus, Absender und Name stehen also ohnehin drin, und es gibt nichts zu prüfen. Bots haben kein Postfach und kein Mailprogramm. Sie füllen Felder aus und schicken Formulare ab, und hier gibt es nichts, was sie abschicken könnten.
Das neue Formular sieht so aus:
<form method="POST" action="#" name="mailto_form" id="contact">
<label for="subject">Betreff</label>
<input id="subject" type="text" name="subject"
oninput="generate_mailto()">
<label for="message">Nachricht</label>
<textarea name="message" id="message" cols="30" rows="10"
oninput="generate_mailto()"></textarea>
<a href="mailto:..." id="mailto-link" role="button">
Mit dem E-Mail-Programm senden</a> an
<a href="mailto:...">...</a>.
</form>Statt nach dem Namen fragen wir nach einer kurzen Zusammenfassung, wie beim Betreff einer E-Mail. Bei jeder Änderung in einem der Felder ruft das Attribut oninput die Funktion generate_mailto() auf. Wir nehmen input statt keyup, weil es auch dann auslöst, wenn jemand Text mit der Maus einfügt, per Autovervollständigung oder per Diktierfunktion schreibt. Mit keyup bliebe der Link in diesen Fällen leer.
Die Funktion generate_mailto()
function generate_mailto() {
var mailto = "mailto:mail@domain.tld"
, subject = encodeURIComponent(document.mailto_form.subject.value)
, message = encodeURIComponent(document.mailto_form.message.value);
mailto += "?subject=" + subject;
mailto += "&body=" + message;
document.getElementById("mailto-link").href = mailto;
}Bei jeder Änderung baut die Funktion den mailto-Link neu: Betreff und Nachricht werden URL-kodiert und als Parameter subject und body angehängt. Klickt jemand auf den Button, öffnet sich das Standard-E-Mail-Programm mit Empfänger, Betreff und Text als fertigem Entwurf.
Die E-Mail-Adresse verschleiern
Bleibt die Empfängeradresse, die bisher im Klartext im HTML steht. Ein Crawler, der die Seite ausliest, findet sie sofort, und schon steht sie auf der nächsten Spam-Liste.
Wir verschleiern sie deshalb, indem wir jedes Zeichen als Unicode-Escape schreiben. Aus „mail@domain.tld“ wird:
\u006d\u0061\u0069\u006c\u0040\u0064\u006f\u006d\u0061\u0069\u006e\u002e\u0074\u006c\u0064Schnell und pragmatisch geht die Umwandlung mit ein paar Zeilen JavaScript:
var mail = "mail@domain.tld";
// In ein Array umwandeln
let unicode = Array.from(mail);
// Jedes Zeichen in Unicode umwandeln
unicode = unicode.map(function(item) {
let charCode = item.charCodeAt(0);
return "\\u" + charCode.toString(16).padStart(4, '0');
});
// Wieder zu einem String zusammensetzen
unicode = unicode.join('');
console.log(unicode);
// \u006d\u0061\u0069\u006c\u0040\u0064\u006f\u006d\u0061\u0069\u006e\u002e\u0074\u006c\u0064Die Umwandlung brauchst du nur einmal, es gibt dafür auch Online-Werkzeuge. Den Wert setzt du dann in generate_mailto() ein. Damit die Adresse trotzdem lesbar auf der Seite steht, schreibt ein kleines Skript sie erst im Browser mit document.write() in den Link:
<a href="javascript:location='mailto:...';void 0">
<script type="text/javascript">
document.write('...')
</script>
</a>Das setzt voraus, dass JavaScript aktiviert ist. Für uns ist das ein vertretbarer Kompromiss. Ehrlicherweise hält die Verschleierung vor allem einfache Crawler ab. Wer die Seite in einem echten Browser rendert, kommt trotzdem an die Adresse, das Risiko sinkt also, verschwindet aber nicht.
Das fertige Formular
Zusammengesetzt sieht die Vorlage so aus:
<form method="post" action="#" name="mailto_form" id="contact">
<label for="subject">Betreff</label>
<input id="subject" type="text" name="subject" required
oninput="generate_mailto()">
<label for="message">Nachricht</label>
<textarea name="message" id="message" cols="30" rows="10" required
maxlength="2000" oninput="generate_mailto()"></textarea>
<!-- E-Mail-Adresse verschleiern -->
<a href="javascript:location='mailto:...';void 0" id="mailto-link" role="button">
Mit dem E-Mail-Programm senden</a> an
<a href="javascript:location='mailto:...';void 0">
<script type="text/javascript">document.write('...')</script>
</a>.
</form>
<script type="text/javascript">
function generate_mailto() {
... // siehe oben
}
</script>Ausprobieren kannst du es auf unserer Kontaktseite. Tipp etwas in Betreff und Nachricht, fahr mit der Maus über den Button und schau dir den Link in der Statusleiste deines Browsers an. In unserem eigenen Skript steckt die Adresse übrigens als Hex-Kodierung (%6B%6F…), die in einer mailto-URL genauso funktioniert.
Wo die Lösung an Grenzen stößt
Nicht jeder hat ein E-Mail-Programm eingerichtet. Wer nur Webmail im Browser nutzt, sieht nach dem Klick unter Umständen gar nichts oder einen Dialog des Betriebssystems. Wir bieten deshalb unter dem Formular WhatsApp als Alternative an, mit derselben Nachricht vorausgefüllt.
Außerdem können sehr lange Nachrichten je nach Browser und Mailprogramm im Link abgeschnitten werden. Deshalb begrenzt maxlength="2000" in der Vorlage das Nachrichtenfeld, für eine erste Anfrage reicht das. Weil URL-Kodierung Umlaute und Sonderzeichen auf mehrere Zeichen aufbläht, wird der fertige Link trotzdem länger als die Nachricht selbst.
Warum das nachhaltiger ist
Nach dem Laden der Seite braucht das Formular keinen Server mehr. Kein Skript nimmt Nachrichten entgegen, keine Datenbank speichert sie, und nichts davon muss aktualisiert, überwacht oder gegen Angriffe abgesichert werden. Auf einer statischen Seite bleibt sie damit statisch.
Es lädt keine Skripte von Drittanbietern, also weder Captcha noch Formulardienst. Jeder Seitenaufruf überträgt weniger, und die Nachricht geht direkt vom Postfach des Besuchers in unseres, ohne Zwischenstation bei einem weiteren Unternehmen.
Wir fragen nur, was wir wirklich brauchen. Name und Adresse kommen mit der E-Mail, wie bei jeder anderen E-Mail auch. Und Spam, der gar nicht erst ankommt, muss weder gefiltert noch gespeichert noch von jemandem gelöscht werden.
Weitere Ansätze für schlankere Seiten haben wir in Wie können Webseiten klimafreundlicher gestaltet werden? gesammelt.
Und die Besucher?
Die Lösung mag altmodisch wirken, aber einige Besucher haben uns erzählt, dass sie sie gerade deshalb erfrischend fanden. Manche fanden es fast magisch, dass ihre Nachricht direkt in eine neue E-Mail übernommen wird. Und weil sie die E-Mail mit ihrem eigenen Programm verschicken, liegt sie danach in ihrem Ordner „Gesendet“. Ob die Nachricht wirklich rausgegangen ist, muss sich niemand fragen.