# So fügst du jeder Landingpage ein Wartelisten-Formular in reinem HTML hinzu

> Eine komplette Wartelisten-Seite in reinem HTML ohne Build, die ohne JavaScript funktioniert, nach dem Laden ein Danke inline zeigt und bei Bedarf zurückfällt.

Veröffentlicht am 2026-10-25 · https://ikiform.com/de/guides/waitlist/html

Schreib ein normales HTML-Formular, das die E-Mail an deinen Ikiform-Endpunkt sendet und auf eine Dankeseite weiterleitet, damit es auf jedem Host ganz ohne JavaScript funktioniert. Füge dann ein kleines Skript hinzu, das dasselbe Formular mit `fetch` sendet, das Danke inline zeigt und an den einfachen Post zurückgibt, wann immer das Skript nicht fertig werden kann, etwa wenn die Menschlichkeitsprüfung aktiv ist.

Eine Wartelisten-Seite braucht kein Framework, keinen Build-Schritt und keinen Server. Vier statische Dateien und ein Formular-Backend reichen, und die Seite lädt schneller als alles, was ein Baukasten exportiert. Diese Seite baut das Ganze Schicht für Schicht, sodass jede Schicht für sich funktioniert.

## Worauf es ohne Build-Schritt ankommt

- **Das Formular ist die Integration.** Ein einfaches `<form>` mit einer `action` ist die ganze Verbindung zu Ikiform. Kein Paket, kein API-Schlüssel, nichts zu kompilieren.
- **JavaScript ist ein Upgrade, keine Voraussetzung.** Die Seite sollte Anmeldungen sammeln, wenn das Skript blockiert, langsam oder kaputt ist. Jede Schicht unten baut auf der vorherigen auf und fällt auf sie zurück.
- **Das Skript darf niemanden festhalten.** Wenn `fetch` scheitert oder Ikiform eine Menschlichkeitsprüfung verlangt, die `fetch` nicht ausführen kann, gibt das Skript das Formular an den Browser zurück, der es auf normalem Weg sendet.
- **Hosting geht überall.** GitHub Pages, Netlify, Cloudflare Pages, ein S3-Bucket oder ein Shared Host liefern diese Dateien so aus, wie sie sind.

| Schicht | Was sie hinzufügt | Wenn sie fehlt |
| --- | --- | --- |
| HTML-Formular | Sendet die E-Mail, leitet zu `thanks.html` weiter | Nichts funktioniert, deshalb kommt es zuerst |
| CSS | Eine lesbare Seite im hellen und dunklen Modus | Ein schlichtes, aber funktionierendes Formular |
| Skript | Danke inline, Feldfehler, kein Seitenwechsel | Der einfache Post und die Weiterleitung |

Diagramm: Die drei Wege, die eine Anmeldung auf einer Wartelisten-Seite in reinem HTML nehmen kann

1. Ohne JavaScript sendet der Browser das Formular, Ikiform leitet mit 303 auf deine _redirect-URL weiter, und der Besucher sieht thanks.html.
2. Ist das Skript geladen, sendet es mit fetch und verlangt JSON. Ikiform antwortet mit 201 oder einem Fehler, und das Skript zeigt das Ergebnis auf derselben Seite.
3. Wenn fetch scheitert oder Ikiform mit challenge_required antwortet, ruft das Skript form.submit() auf.
4. Dieser einfache Post führt bei Bedarf Ikiforms Prüfseite aus und endet auf thanks.html.

Jeder Weg endet mit einer gespeicherten Anmeldung. Das Skript ändert nur, wie der Besucher sie sieht.

## Die Dateien

### Die Seite

`index.html`:

```html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Acme is almost here</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main>
    <h1>Acme is almost here</h1>
    <p>Invoices that chase themselves. Leave your email and we'll invite you first.</p>

    <form id="waitlist" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
      <label for="email">Email</label>
      <div class="row">
        <input id="email" name="email" type="email" autocomplete="email" required placeholder="you@company.com">
        <button>Join the waitlist</button>
      </div>
      <input type="hidden" name="_redirect" value="https://example.com/thanks.html">
      <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden>
      <p class="message" role="status" aria-live="polite"></p>
    </form>
  </main>
  <script src="waitlist.js" defer></script>
</body>
</html>
```

- `_redirect` schickt den Browser nach einem einfachen Post zu `thanks.html`. Es funktioniert nur für eine `https:`-URL auf einer der **Allowed domains** des Formulars, füge deine Domain also dort hinzu. **Redirect after submit** in den Einstellungen des Formulars tut dasselbe ohne das versteckte Eingabefeld.
- Das Eingabefeld `_gotcha` ist der Honeypot. Menschen sehen es nie; Bots, die jedes Eingabefeld ausfüllen, werden als Spam gespeichert.
- `aria-live="polite"` lässt Screenreader die Meldungen des Skripts ansagen, ohne den Fokus zu verschieben.
- Das Skript-Tag nutzt `defer`, es läuft also, nachdem das Formular existiert, und blockiert nie das Zeichnen der Seite.

### Die Erweiterung

`waitlist.js`:

```js
const form = document.getElementById("waitlist");
const message = form.querySelector(".message");
const button = form.querySelector("button");
const key = crypto.randomUUID();

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  button.disabled = true;
  message.textContent = "Joining…";
  try {
    const response = await fetch(form.action, {
      method: "POST",
      headers: { Accept: "application/json", "Idempotency-Key": key },
      body: new FormData(form),
    });
    const result = await response.json();
    if (result.ok) {
      form.replaceChildren(Object.assign(document.createElement("p"), {
        textContent: "You're on the list. We'll email you on launch day.",
        role: "status",
      }));
      return;
    }
    if (result.error.code === "challenge_required") {
      form.submit();
      return;
    }
    const wait = response.headers.get("Retry-After");
    message.textContent = wait
      ? `Too many tries. Wait ${wait} seconds and try again.`
      : result.error.fields?.email?.[0] ?? result.error.message;
  } catch {
    form.submit();
    return;
  }
  button.disabled = false;
});
```

- `Accept: application/json` lässt Ikiform mit JSON statt mit einer Weiterleitung antworten, damit das Skript das Ergebnis an Ort und Stelle zeigen kann.
- Der Idempotenzschlüssel wird einmal pro Seitenaufruf erzeugt. Ein Doppelklick oder ein erneuter Versuch nach einer langsamen Antwort speichert eine Anmeldung.
- `challenge_required` heißt, die Spamprüfung des Formulars steht auf **Ikiform** und `fetch` hat kein Token gesendet. `form.submit()` sendet auf die einfache Art, was Ikiforms Prüfseite und dann die Dankeseite zeigt. Es löst das `submit`-Event nicht erneut aus, es gibt also keine Schleife.
- Ein Netzwerkfehler nimmt denselben Fallback. War Ikiform nicht erreichbar, zeigt der einfache Post den eigenen Fehler des Browsers, was immer noch ehrlicher ist als ein hängender Button.
- Ein `429` liest `Retry-After` und sagt, wie lange zu warten ist.

### Die Dankeseite

`thanks.html`:

```html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>You're on the list</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main>
    <h1>You're on the list</h1>
    <p>We'll email you the day Acme opens. Nothing before that.</p>
    <p><a href="/">Back to the page</a></p>
  </main>
</body>
</html>
```

### Die Styles

`style.css`:

```text
body { margin: 0; font: 16px/1.5 system-ui, sans-serif; color: #111; background: #fff; }
main { max-width: 34rem; margin: 0 auto; padding: 18vh 1rem 4rem; }
h1 { font-size: 2.25rem; line-height: 1.1; margin: 0 0 0.75rem; }
label { display: block; font-weight: 600; margin: 1.5rem 0 0.375rem; }
.row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
input[type="email"] { flex: 1 1 14rem; font: inherit; padding: 0.625rem 0.75rem; border: 1px solid #ccc; border-radius: 0.5rem; }
button { font: inherit; padding: 0.625rem 1rem; border: 0; border-radius: 0.5rem; background: #111; color: #fff; cursor: pointer; }
button:disabled { opacity: 0.6; cursor: progress; }
.message { min-height: 1.5em; margin: 0.5rem 0 0; }
@media (prefers-color-scheme: dark) {
  body { color: #eee; background: #111; }
  input[type="email"] { background: #1b1b1b; color: inherit; border-color: #444; }
  button { background: #eee; color: #111; }
}
```

> Wenn die Seite auf einer anderen Domain als `example.com` liegt, passe `_redirect` und den Link zurück an. Ikiform ignoriert ein `_redirect`, dem es nicht folgen kann, und zeigt stattdessen seine eigene gehostete Dankeseite, eine Abweichung kostet also nie eine Anmeldung.

## Deployen und testen

Leg die vier Dateien in einen Ordner und lade ihn auf einen beliebigen statischen Host hoch. Füge dann die Domain des Hosts unter **Allowed domains** hinzu: zum Beispiel `username.github.io`, `your-site.netlify.app` oder deine eigene Domain. Solange du lokal arbeitest, füge auch `localhost` hinzu; liefere den Ordner mit einem beliebigen statischen Server aus, denn eine als `file://`-URL geöffnete Seite sendet keinen Origin.

## Prüfen, ob es funktioniert

1. Füge unter **Allowed domains** des Formulars die Domain deiner Site hinzu, plus `localhost`, während du entwickelst. Lass das Feld leer, um Posts von überall anzunehmen.
2. Schalte im Browser JavaScript aus und melde dich an. Du landest auf `thanks.html`.
3. Schalte es wieder ein und melde dich erneut an. Das Formular wird durch die Dankeszeile ersetzt, ohne die Seite zu verlassen.
4. Setze **Spam check** auf **Ikiform** und melde dich noch einmal an. Die Seite wechselt zur Prüfung, dann zu `thanks.html`.
5. Öffne **Submissions** im Dashboard. Die neue Einsendung ist da, mit jedem Feld, das du gesendet hast.
6. Sende einen Post mit ausgefülltem Honeypot, `_gotcha=bot`. Er bekommt trotzdem eine Erfolgsantwort, aber die Einsendung wird als Spam gespeichert und löst weder E-Mail noch Webhook aus.

## Stolperfallen

| Problem | Was zu tun ist |
| --- | --- |
| Der Browser landet auf der Dankeseite von Ikiform statt auf deiner | `_redirect` wird ignoriert, außer seine Domain steht unter **Allowed domains** und es ist eine `https:`-URL. Füge die Domain hinzu oder setze stattdessen **Redirect after submit** in den Einstellungen des Formulars. |
| `403 origin_not_allowed`, oder jeder Post landet im Spam | Die Domain der Seite steht nicht unter **Allowed domains** des Formulars. Füge sie hinzu, plus `localhost` für lokale Tests. Subdomains einer eingetragenen Domain sind erlaubt. |
| Posts von einer als Datei geöffneten Seite werden abgelehnt oder als Spam markiert | Eine `file://`-Seite sendet keinen Origin. Liefere den Ordner mit einem lokalen statischen Server aus und füge `localhost` zu **Allowed domains** hinzu. |
| Die Dankeszeile erscheint nie, aber Anmeldungen kommen an | Das Skript wurde nicht geladen: Prüfe den Pfad im `src`-Attribut oder ob der Host `.js`-Dateien ausliefert. Der einfache Post funktioniert weiterhin, deshalb kommen Anmeldungen an. |
| `402 test_limit_reached` | Der Workspace hat keinen Tarif und hat seine 50 kostenlosen `localhost`-Tests diesen Monat aufgebraucht. Wähle einen Tarif oder nutze **Send test** im Dashboard. |

Die [Doku zu HTML-Formularen](https://ikiform.com/de/docs/html.md) listet jedes reservierte Feld auf, und die [Wartelisten-Vorlage](https://ikiform.com/forms/waitlist) erzeugt eine Version mit Name und Rolle. Export der Liste und die Launch-E-Mail stehen in der [Hauptanleitung](https://ikiform.com/de/guides/waitlist.md#launch-day).

## Fragen

### Kann ein Wartelisten-Formular ohne JavaScript funktionieren?

Ja. Ein einfaches HTML-Formular sendet die E-Mail an deinen Ikiform-Endpunkt, und Ikiform leitet den Browser auf deine Dankeseite weiter. JavaScript fügt nur die Inline-Meldung hinzu.

### Wo kann ich eine Wartelisten-Seite in reinem HTML hosten?

Überall, wo statische Dateien ausgeliefert werden: GitHub Pages, Netlify, Cloudflare Pages, S3 oder ein Shared Host. Das Formular sendet an Ikiform, der Host führt also nie Code aus.

### Was passiert, wenn die Menschlichkeitsprüfung aktiv ist und das Skript mit fetch sendet?

Ikiform antwortet mit 403 challenge_required. Das Skript in dieser Anleitung fängt das ab und sendet das Formular auf die einfache Art, was die Prüfseite und dann deine Dankeseite zeigt.

### Hostet Ikiform die Formularseite für mich?

Nein. Ikiform hat keine gehosteten Formularseiten. Du setzt das Formular auf deine eigene Site, in jedem Baukasten oder Framework, und richtest es auf deinen Ikiform-Endpunkt aus.

### Ist Ikiform kostenlos?

Nein, es gibt keinen kostenlosen Tarif. Testen ist kostenlos: Du kannst 50 Testeinsendungen pro Monat von localhost senden, bevor du zahlst. Für den Livebetrieb startet Pro bei $6/Monat für 500 Einsendungen, oder du zahlst einmalig 10.000 Einsendungen für $49 oder 50.000 Einsendungen für $149, mit Guthaben, das nie verfällt.

## Weitere Anleitungen

- [Warteliste für ein neues SaaS](https://ikiform.com/de/guides/waitlist.md): Baue eine Pre-Launch-Warteliste auf deiner eigenen Landingpage: das Formular, Spamschutz, Duplikate, Bestätigungs-E-Mails und ein sauberer Export am Launch-Tag.
- [Warteliste mit Next.js](https://ikiform.com/de/guides/waitlist/nextjs.md): Eine Warteliste mit dem Next.js App Router: eine Server Action, die Anmeldungen mit Secret Key weiterleitet, useActionState, ein optimistisches Danke und ein Limit pro Besucher.
- [Warteliste auf Carrd](https://ikiform.com/de/guides/waitlist/carrd.md): Sammle Wartelisten-Anmeldungen auf einer Carrd-Seite: ein Send-to-URL-Formular auf Pro Plus mit UTM-Tracking oder ein eingebettetes HTML-Formular mit Honeypot auf Pro Standard.
- [Launch-Liste für Product Hunt](https://ikiform.com/de/guides/waitlist/product-hunt.md): Sammle Wochen vor Product Hunt eine Launch-Liste, markiere jede Anmeldung nach Kanal und sende die Launch-E-Mail in zwei Wellen, abgestimmt auf den Reset nach Pacific Time.
- [Beta-Anmeldung mit Auswahl](https://ikiform.com/de/guides/waitlist/beta-signup.md): Ein Beta-Bewerbungsformular mit Auswahlfragen, die der Server durchsetzt, und ein Skript, das Bewerber bewertet und jede Kohorte aus der Liste auswählt.
- [Warteliste für mobile Apps](https://ikiform.com/de/guides/waitlist/mobile-app.md): Eine Pre-Launch-Warteliste für eine iOS- oder Android-App, die fragt, welches Handy jemand nutzt, iPhone-Tester zu TestFlight schickt und einen Play-Closed-Test füllt.
