GitHub Pages stellt ausschließlich statische Dateien wie HTML, CSS und JavaScript bereit. Ein Kontaktformular kann daher nicht direkt über GitHub Pages E-Mails versenden oder Formulardaten serverseitig verarbeiten.
Für ein Kontaktformular wird deshalb ein externer Formular-Dienst benötigt. Eine einfache und für eine statische Website gut geeignete Lösung ist Formspree.
Der wesentliche Vorteil: Die private Empfängeradresse muss weder im HTML noch im JavaScript der Website stehen.
Das Formular auf der Website sendet die eingegebenen Daten nicht direkt an eine E-Mail-Adresse, sondern an einen Formspree-Endpunkt.
Website
↓
Kontaktformular
↓
Formspree
↓
private E-Mail-Adresse
Im öffentlich sichtbaren HTML steht dabei lediglich eine Formular-ID, beispielsweise:
https://formspree.io/f/abcdwxyz
Die dahinter hinterlegte Empfängeradresse bleibt für Besucher der Website verborgen.
contact.html eintragen.Der relevante HTML-Code sieht beispielsweise so aus:
<form
action="https://formspree.io/f/DEINE_FORM_ID"
method="POST"
>
DEINE_FORM_ID wird durch die von Formspree bereitgestellte ID ersetzt.
Die Formular-ID selbst darf öffentlich sein. Sie ist nicht mit der privaten E-Mail-Adresse gleichzusetzen.
Das folgende Formular kann direkt in eine statische GitHub-Pages-Seite eingebaut werden.
<section class="card" aria-labelledby="contact-form-title">
<h2 class="card-title" id="contact-form-title">
Send a message
</h2>
<form
class="contact-form"
action="https://formspree.io/f/DEINE_FORM_ID"
method="POST"
>
<div class="form-field">
<label for="contact-name">Name</label>
<input
type="text"
id="contact-name"
name="name"
autocomplete="name"
maxlength="100"
required
>
</div>
<div class="form-field">
<label for="contact-email">Email address</label>
<input
type="email"
id="contact-email"
name="email"
autocomplete="email"
maxlength="254"
required
>
<p class="form-help">
Your address is used only so that I can reply to your message.
</p>
</div>
<div class="form-field">
<label for="contact-subject">Subject</label>
<input
type="text"
id="contact-subject"
name="subject"
maxlength="150"
required
>
</div>
<div class="form-field">
<label for="contact-message">Message</label>
<textarea
id="contact-message"
name="message"
rows="9"
minlength="10"
maxlength="5000"
required
></textarea>
</div>
<div class="form-honeypot" aria-hidden="true">
<label for="contact-website">
Leave this field empty
</label>
<input
type="text"
id="contact-website"
name="_gotcha"
tabindex="-1"
autocomplete="off"
>
</div>
<p class="form-privacy">
The information entered here is transmitted through Formspree
for the purpose of processing and answering the message.
Further information can be found in the
<a href="privacy.html">privacy notice</a>.
</p>
<button class="form-submit" type="submit">
Send message
</button>
</form>
</section>
Wichtig ist insbesondere das name-Attribut jedes Formularfeldes.
Nur Felder mit einem solchen Attribut werden beim Absenden übertragen.
Das Formular kann mit den bereits vorhandenen CSS-Variablen und Card-Stilen der Website gestaltet werden.
/* =========================================================
CONTACT FORM
========================================================= */
.contact-form {
display: grid;
gap: 1.25rem;
margin-top: 1.25rem;
}
.form-field {
display: grid;
gap: 0.4rem;
}
.form-field label {
font-weight: 650;
}
.form-field input,
.form-field textarea {
width: 100%;
padding: 0.75rem 0.85rem;
font: inherit;
line-height: 1.5;
color: var(--text);
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
transition:
border-color 120ms ease,
box-shadow 120ms ease;
}
.form-field textarea {
min-height: 180px;
resize: vertical;
}
.form-field input:hover,
.form-field textarea:hover {
border-color: rgba(31, 35, 40, 0.35);
}
.form-field input:focus,
.form-field textarea:focus {
outline: none;
border-color: var(--link);
box-shadow: 0 0 0 3px rgba(11, 95, 255, 0.14);
}
.form-help,
.form-privacy {
margin: 0;
color: var(--muted);
font-size: 0.9rem;
}
.form-privacy {
max-width: 70ch;
}
.form-honeypot {
position: absolute;
left: -10000px;
width: 1px;
height: 1px;
overflow: hidden;
}
.form-submit {
justify-self: start;
padding: 0.7rem 1.15rem;
font: inherit;
font-weight: 650;
color: #ffffff;
background: var(--text);
border: 1px solid var(--text);
border-radius: 10px;
cursor: pointer;
transition:
transform 120ms ease,
opacity 120ms ease;
}
.form-submit:hover {
opacity: 0.88;
transform: translateY(-1px);
}
.form-submit:active {
transform: translateY(0);
}
.form-submit:focus-visible {
outline: 3px solid rgba(11, 95, 255, 0.45);
outline-offset: 3px;
}
Öffentliche Kontaktformulare werden früher oder später von Bots gefunden. Ein gewisser Spam-Schutz sollte daher von Beginn an eingeplant werden.
Eine einfache Maßnahme ist ein unsichtbares zusätzliches Formularfeld:
<input
type="text"
name="_gotcha"
tabindex="-1"
autocomplete="off"
>
Normale Besucher sehen dieses Feld nicht und lassen es leer. Viele einfache Bots füllen hingegen automatisch sämtliche Formularfelder aus. Formspree kann solche Einsendungen anschließend als Spam erkennen.
Das Honeypot-Feld sollte nicht einfach mit display: none entfernt werden,
sondern beispielsweise außerhalb des sichtbaren Bereichs positioniert bleiben.
Cloudflare Turnstile oder ähnliche Systeme sind bei einer rein statischen Website nicht automatisch eine bessere Lösung. Die eigentliche Token-Prüfung sollte serverseitig erfolgen. Ein lediglich eingeblendetes Widget ohne sichere Validierung wäre eher Kulisse als Schutz.
Bei Verwendung eines Formular-Backends wie Formspree muss die private Empfängeradresse nicht öffentlich auf der Website erscheinen.
Der Formular-Dienst selbst kennt die Empfängeradresse selbstverständlich, da er die eingegangenen Nachrichten zustellen muss.
Wird auf eine Kontaktanfrage direkt aus dem privaten E-Mail-Konto geantwortet, sieht der Empfänger anschließend normalerweise die Absenderadresse.
Wer die private Adresse auch beim Antworten verborgen halten möchte, sollte daher eine eigene öffentliche Kontaktadresse oder einen Alias verwenden.
contact@deine-domain.example
Diese Adresse kann beispielsweise an das eigentliche private Postfach weiterleiten. Damit bleibt das persönliche Konto sowohl auf der Website als auch in der späteren Kommunikation im Hintergrund.
Über ein Kontaktformular werden personenbezogene Daten verarbeitet. Dazu gehören insbesondere:
Da die Formulardaten über einen externen Dienst verarbeitet werden, sollte dies in der Datenschutzerklärung der Website berücksichtigt werden.
Sinnvoll ist außerdem ein kurzer Hinweis unmittelbar beim Formular:
<p class="form-privacy">
The information entered here is transmitted through Formspree
for the purpose of processing and answering the message.
Further information can be found in the
<a href="privacy.html">privacy notice</a>.
</p>
Die Datenschutzerklärung sollte insbesondere erläutern:
Eine verpflichtende Checkbox mit „Ich stimme der Verarbeitung zu“ sollte nicht reflexartig eingebaut werden. Für die Bearbeitung einer freiwilligen Kontaktanfrage ist eine Einwilligung nicht zwingend in jedem Fall die sachlich passende Rechtsgrundlage.
Für eine statische GitHub-Pages-Website bietet sich folgende Architektur an:
contact.html
│
├── Name
├── E-Mail
├── Betreff
├── Nachricht
└── Honeypot
│
▼
Formspree
│
▼
Kontakt-Alias
│
▼
privates Postfach
Diese Lösung bleibt technisch schlank und passt gut zu einer Website ohne Frameworks, Build-System oder eigenes Backend.
Für eine kleine persönliche Projekt- und Dokumentationsseite ist ein eigenes serverseitiges Kontakt-Backend in der Regel unnötig. Ein externer Formular-Endpunkt erfüllt denselben Zweck mit deutlich weniger Wartungsaufwand.
Kurzfassung: GitHub Pages rendert das Formular, Formspree verarbeitet die Nachricht, und ein separater Kontakt-Alias schützt die eigentliche private E-Mail-Adresse.