Praxisanleitung

WordPress-Kontaktformular ohne Plugin

Formular in „Individuelles HTML“ einsetzen oder die Sendung im Child-Theme verarbeiten. Ohne Contact Form 7. Der Mailversand des Hosts bleibt heikel.

Die kurze Antwort. Ja. Setzen Sie das Formular in den Block „Individuelles HTML“, oder verarbeiten Sie die Sendung im Theme. Der Block geht an einen Formular-Dienst, ganz ohne Plugin. wp_mail() in der functions.php heißt nur, dass PHP die Mail angenommen hat. Ohne SMTP landet sie oft im Spam. Im Free-Tarif ist Telegram sofort da, die E-Mail eine Tagesübersicht am nächsten Morgen um 08:00.

Formgong teamAktualisiert

WordPress-Kontaktformular ohne Plugin
WordPress-Kontaktformular ohne Plugin

Zwei Wege, ohne Plugin

WordPress bringt im Kern kein Kontaktformular mit. Die Blockliste nennt Text, Medien, Design, Widgets, Theme und Einbettungen. Das Kommentarformular ist etwas anderes. Ein Kontaktformular bauen Sie selbst, oder Sie nehmen ein Plugin.

Ohne Plugin bleiben zwei ehrliche Wege. Sie lösen nicht dieselbe Aufgabe.

  • Block „Individuelles HTML“. Das Formular ist Markup. Es sendet an einen Dienst, den Sie nicht selbst betreiben. Sie schreiben kein PHP und installieren kein Formular-Plugin. Das ist der Weg unten.
  • Verarbeitung im Theme. Das Formular sendet an admin-post.php. Das Child-Theme prüft einen Nonce, bereinigt die Felder und ruft wp_mail auf. Diesen Code pflegen Sie.

Contact Form 7 ist ein dritter Weg. Er passt, wenn die Anfragen in WordPress bleiben sollen und Sie das Plugin aktuell halten. Diese Seite ist der andere Auftrag. Die allgemeine Einrichtung, samt Webhook in Elementor Pro, steht auf der WordPress-Seite.

Wenn die Website gar kein WordPress ist, vergleicht Formular ohne Backend die Empfänger. Dieselbe Sendung beschreiben das HTML-Kontaktformular und das JavaScript-Kontaktformular.

Individuelles HTML, mit und ohne Skript

Das Formular unten ist zum Einfügen gedacht. Es hat zwei Verhalten. Bleibt das Skript gespeichert, hält fetch die Besucher auf der Seite und schreibt das Ergebnis in eine Live-Region. Löscht WordPress das Skript, hat das Formular weiter method="POST" und eine action. Der Browser sendet es dann ganz normal.

Ersetzen Sie fk_your_access_key. Lassen Sie botcheck leer. Den Datenschutz-Link tauschen Sie gegen Ihre eigene Seite. Setzen Sie Content-Type nicht selbst. FormData setzt die Grenze.

  1. Seite öffnen. Bearbeiten Sie die Seite oder den Beitrag. Öffnen Sie den Block-Inserter und suchen Sie nach HTML.
  2. Individuelles HTML einfügen. Fügen Sie den Block „Individuelles HTML“ ein. Im klassischen Editor nutzen Sie den Tab Text. In Elementor das HTML-Widget.
  3. Formular einsetzen. Fügen Sie das Formular ein, ersetzen Sie den Zugangsschlüssel und setzen Sie den Link auf Ihre eigene Datenschutzerklärung.
  4. Speichern und nachsehen. Aktualisieren Sie die Seite. Fehlt das Formular oder das Skript, lesen Sie zuerst den Abschnitt zu den Grenzen.
  5. Auf der Live-URL testen. Senden Sie von der veröffentlichten Seite. Prüfen Sie Posteingang oder Telegram, nicht nur den Text auf der Seite.

Im Block-Editor suchen Sie nach HTML und wählen Individuelles HTML. In der englischen Oberfläche heißt der Block Custom HTML. Die Anleitung von WordPress.com, zuletzt geprüft am 5. Oktober 2026, beschreibt genau diese Suche. Im klassischen Editor öffnen Sie den Tab Text, sonst wird der Code als Text angezeigt. In Elementor nimmt das HTML-Widget denselben Code. Divi und andere Builder haben ein Code- oder HTML-Modul.

Die Vorschau im Editor ist kein Test. Öffnen Sie die veröffentlichte Adresse und senden Sie eine Nachricht, die Sie wiedererkennen.

Eine Danke-Seite setzen Sie mit einem versteckten Feld _redirect. Die Adresse muss auf Ihrer Website liegen. Das Muster steht in Danke-Seite nach dem Absenden.

HTML
<form id="contact" action="https://formgong.com/submit" method="POST">
  <input type="hidden" name="access_key" value="fk_your_access_key">
  <input type="hidden" name="_lang" value="de">
  <label>Name <input name="name" autocomplete="name" required></label>
  <label>E-Mail <input type="email" name="email" autocomplete="email" required></label>
  <label>Nachricht <textarea name="message" required></textarea></label>
  <p>Wir nutzen Ihre Angaben nur, um zu antworten. <a href="/privacy">Datenschutzerklärung</a>.</p>
  <div aria-hidden="true" style="position:absolute;inset-inline-start:0;top:0;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)">
    <label>Bitte leer lassen <input name="botcheck" tabindex="-1" autocomplete="off"></label>
  </div>
  <button type="submit">Senden</button>
  <p id="form-status" role="status" tabindex="-1"></p>
</form>
<script>
  var form = document.getElementById("contact");
  var button = form.querySelector("button[type=submit]");
  var status = document.getElementById("form-status");
  form.addEventListener("submit", function (event) {
    event.preventDefault();
    button.disabled = true;
    status.textContent = "Wird gesendet …";
    fetch(form.action, {
      method: "POST",
      body: new FormData(form),
      headers: { Accept: "application/json" }
    }).then(function (response) {
      return response.json().then(function (result) {
        return { ok: response.ok, result: result };
      });
    }).then(function (payload) {
      if (!payload.ok || !payload.result || payload.result.success !== true) {
        status.textContent = (payload.result && payload.result.message) || "Bitte versuchen Sie es erneut.";
        button.disabled = false;
        status.focus();
        return;
      }
      form.reset();
      status.textContent = payload.result.message || "Danke. Wir haben Ihre Nachricht.";
      status.focus();
    }).catch(function () {
      status.textContent = "Das Formular konnte nicht gesendet werden. Bitte versuchen Sie es erneut.";
      button.disabled = false;
      status.focus();
    });
  });
</script>
Dasselbe Formular ohne Skript, falls der Editor JavaScript löscht
HTML
<form action="https://formgong.com/submit" method="POST">
  <input type="hidden" name="access_key" value="fk_your_access_key">
  <input type="hidden" name="_lang" value="de">
  <label>Name <input name="name" autocomplete="name" required></label>
  <label>E-Mail <input type="email" name="email" autocomplete="email" required></label>
  <label>Nachricht <textarea name="message" required></textarea></label>
  <p>Wir nutzen Ihre Angaben nur, um zu antworten. <a href="/datenschutz">Datenschutzerklärung</a>.</p>
  <div aria-hidden="true" style="position:absolute;inset-inline-start:0;top:0;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)">
    <label>Bitte leer lassen <input name="botcheck" tabindex="-1" autocomplete="off"></label>
  </div>
  <button type="submit">Senden</button>
</form>
Den Listener aus dem Child-Theme laden, nicht aus dem Block
PHP
add_action('wp_enqueue_scripts', function () {
  if (!is_page('contact')) return;
  wp_enqueue_script(
    'site-contact',
    get_stylesheet_directory_uri() . '/contact.js',
    array(),
    null,
    true
  );
});
contact.js im Child-Theme
JavaScript
var form = document.getElementById("contact");
if (form) {
  var button = form.querySelector("button[type=submit]");
  var status = document.getElementById("form-status");
  form.addEventListener("submit", function (event) {
    event.preventDefault();
    button.disabled = true;
    status.textContent = "Wird gesendet …";
    fetch(form.action, {
      method: "POST",
      body: new FormData(form),
      headers: { Accept: "application/json" }
    }).then(function (response) {
      return response.json().then(function (result) {
        return { ok: response.ok, result: result };
      });
    }).then(function (payload) {
      if (!payload.ok || !payload.result || payload.result.success !== true) {
        status.textContent = (payload.result && payload.result.message) || "Bitte versuchen Sie es erneut.";
        button.disabled = false;
        status.focus();
        return;
      }
      form.reset();
      status.textContent = payload.result.message || "Danke. Wir haben Ihre Nachricht.";
      status.focus();
    }).catch(function () {
      status.textContent = "Das Formular konnte nicht gesendet werden. Bitte versuchen Sie es erneut.";
      button.disabled = false;
      status.focus();
    });
  });
}

Was WordPress aus dem Block löscht

Geprüft an der WordPress-Dokumentation am 6. Oktober 2026.

  • WordPress.com. Die Anleitung zum individuellen HTML sagt, dass form, input, textarea und script einen bezahlten Tarif mit aktiven Hosting-Funktionen brauchen. Im kostenlosen Tarif verschwinden diese Tags beim Speichern. Dieselbe Anleitung sagt, die Hosting-Funktionen schalten sich ein, nachdem Sie mindestens ein Plugin installiert haben. Ein Formular ganz ohne Plugin ist auf WordPress.com Free über diesen Block nicht möglich. Der Formular-Block dort ist eine eigene Funktion von WordPress.com. In der Blockliste des Kerns steht er nicht.
  • Selbst gehostet, Formular-Tags. wp_kses_allowed_html() erlaubt ein form im Beitrag, wenn input oder select erlaubt ist. Ein Autor kann das Formular speichern. Ein Skript kann er nicht speichern.
  • Selbst gehostet, Skript-Tags. unfiltered_html erlaubt JavaScript im Inhalt. Die Rollen-Seite gibt das auf einer einzelnen Website Administratoren und Redakteuren. Im Multisite-Netz haben es nur Super-Admins. Alle anderen verlieren das Skript beim Speichern. Das Formular ohne Skript sendet trotzdem.
  • Child-Theme. Änderungen an der functions.php gehören ins Child-Theme. Ein Update des Eltern-Themes ersetzt die Datei des Eltern-Themes. PHP gehört nicht in den HTML-Block. Dort wird es als Text gezeigt oder gelöscht.

Wenn der Code verschwindet, fügen Sie ihn nicht zehnmal neu ein. Prüfen Sie auf WordPress.com den Tarif, auf der eigenen Installation die Rolle, und nehmen Sie dann das Formular ohne Skript oder die Theme-Datei.

functions.php, Nonce und wp_mail

Diesen Weg nehmen Sie, wenn die Anfrage auf Ihrem Host bleiben muss und Sie kein weiteres Konto wollen. Der Code gehört ins Child-Theme. Auf der Seite setzen Sie einen Shortcode-Block mit [site_contact_form]. Der Block „Individuelles HTML“ kann den Nonce nicht ausgeben, weil der Nonce PHP ist.

Der Handler macht fünf Dinge. Er prüft den Nonce, also einen Wert gegen fremde Absender. Er wirft ein gefülltes Honigtopf-Feld weg, ohne das zu sagen. Er bereinigt die Felder. Er lehnt einen leeren Namen, eine ungültige E-Mail oder eine leere Nachricht ab. Er leitet nach dem POST weiter, damit ein Neuladen das Formular nicht noch einmal sendet.

wp_mail() ist „ähnlich der mail-Funktion von PHP“. Die Referenz sagt: true heißt, die Methode hat den Auftrag angenommen. Es heißt nicht, dass die Mail angekommen ist. Genau deshalb wirkt ein WordPress-Kontaktformular in Ordnung, und die E-Mail kommt trotzdem nicht an.

Besucher ohne Anmeldung laufen über admin-post-nopriv. Angemeldete Nutzer über admin-post. Registrieren Sie beide. Das Beispiel nimmt die Admin-Adresse aus den Einstellungen. Tauschen Sie sie, wenn Sie ein anderes Postfach lesen.

Child-Theme: Shortcode und admin-post-Handler
PHP
add_shortcode('site_contact_form', function () {
  $error = isset($_GET['form']) && $_GET['form'] === 'error';
  ob_start();
  if ($error) {
    echo '<p role="alert">Bitte prüfen Sie die Felder und senden Sie erneut.</p>';
  }
  ?>
  <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
    <input type="hidden" name="action" value="site_contact">
    <?php wp_nonce_field('site_contact', 'contact_nonce'); ?>
    <label>Name <input name="name" autocomplete="name" required></label>
    <label>E-Mail <input type="email" name="email" autocomplete="email" required></label>
    <label>Nachricht <textarea name="message" required></textarea></label>
    <div aria-hidden="true" style="position:absolute;inset-inline-start:0;top:0;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)">
      <label>Bitte leer lassen <input name="botcheck" tabindex="-1" autocomplete="off"></label>
    </div>
    <button type="submit">Senden</button>
  </form>
  <?php
  return ob_get_clean();
});

add_action('admin_post_nopriv_site_contact', 'site_contact_handle');
add_action('admin_post_site_contact', 'site_contact_handle');

function site_contact_handle() {
  $nonce = isset($_POST['contact_nonce']) ? sanitize_text_field(wp_unslash($_POST['contact_nonce'])) : '';
  if (!wp_verify_nonce($nonce, 'site_contact')) {
    wp_die(esc_html('Ungültige Anfrage.'), '', array('response' => 403));
  }
  $back = wp_get_referer();
  if (!is_string($back) || $back === '') $back = home_url('/');
  if (!empty($_POST['botcheck'])) {
    wp_safe_redirect(home_url('/'));
    exit;
  }
  $name = isset($_POST['name']) ? sanitize_text_field(wp_unslash($_POST['name'])) : '';
  $email = isset($_POST['email']) ? sanitize_email(wp_unslash($_POST['email'])) : '';
  $message = isset($_POST['message']) ? sanitize_textarea_field(wp_unslash($_POST['message'])) : '';
  if ($name === '' || !is_email($email) || $message === '') {
    wp_safe_redirect(add_query_arg('form', 'error', $back));
    exit;
  }
  $sent = wp_mail(
    get_option('admin_email'),
    'Kontaktformular: ' . $name,
    "Name: {$name}\nEmail: {$email}\n\n{$message}",
    array('Reply-To: ' . $email)
  );
  if (!$sent) {
    wp_safe_redirect(add_query_arg('form', 'error', $back));
    exit;
  }
  wp_safe_redirect(home_url('/thanks/'));
  exit;
}

Spam und SMTP, wenn die Mail nicht ankommt

Ein öffentliches Formular wird gefunden. Ein Honigtopf ist die kleine Prüfung, die ohne Plugin auskommt.

  • Nennen Sie das Feld botcheck. Menschen sehen es nicht. Bots füllen es oft. display:none allein reicht nicht. Das Beispiel liegt außerhalb des Bildschirms, hat tabindex="-1" und schaltet die Autovervollständigung aus.
  • Im PHP-Weg so tun, als wäre alles in Ordnung. Leiten Sie auf die Startseite und speichern Sie nichts. Eine Fehlermeldung bringt den Bot dazu, das Feld zu überspringen.
  • Beim Formgong-Weg das Feld leer lassen. Ein gefüllter Honigtopf wird als Spam gespeichert und weder per E-Mail noch an Telegram geschickt.
  • Host-Mail und Spam sind zwei Baustellen. wp_mail nutzt die Mail des Servers. Die Absenderadresse ist oft der Host, nicht Ihre Domain. Empfänger legen die Mail dann in den Spam oder verwerfen sie. Ein SMTP-Plugin kann das richten. Es ist aber wieder ein Plugin, und diese Seite will genau das vermeiden.

Wenn die Mail scheitert und Sie nicht wissen, an welcher Stelle, hilft Kontaktformular sendet keine E-Mail. Der Hinweis zur DSGVO steht im nächsten Abschnitt.

Warum nicht nur Contact Form 7

Contact Form 7 ist eine normale Wahl. Pflegefrei ist es nicht. Die Tabelle zeigt den Tausch, keine Rangliste.

Contact Form 7 neben den zwei Wegen ohne Plugin. WordPress-Verhalten geprüft am 6. Oktober 2026.
Contact Form 7Individuelles HTMLfunctions.php und wp_mail
Was Sie pflegenDas Plugin, oft noch ein zweites für SMTP oder Speicherung.Das Markup. Der Empfänger ist ein Dienst.Den Theme-Code, bei jeder WordPress-Änderung.
E-MailÜber die WordPress-Mail, außer Sie ergänzen SMTP.Der Dienst sendet sie. Im Free-Tarif von Formgong ist das eine Tagesübersicht, nicht eine Mail pro Anfrage.wp_mail. Wahr ist keine Zustellung.
SpamSie bauen einen Filter ein. Die Doku zeigt eine Turnstile-Anbindung.Honigtopf im Markup. Der Dienst kann zusätzlich filtern.Nur das, was Sie schreiben. Im Beispiel ein Honigtopf.
Wo die Anfrage liegtIn der E-Mail, außer ein weiteres Plugin speichert sie.Im Posteingang des Dienstes. Formgong behält sie im Free-Tarif 30 Tage.Nur in der E-Mail, außer Sie bauen Speicher selbst.
TelegramNicht eingebaut. Ein Webhook-Plugin ist eine andere Anleitung.Formgong sendet Telegram im Free-Tarif, sofort.Nur wenn Sie die Bot-API selbst aufrufen.

Wenn Contact Form 7 schon läuft und Sie nur Telegram wollen, behalten Sie das Plugin und folgen Sie Contact Form 7 und Elementor an Telegram. Dafür bauen Sie das Formular nicht neu.

DSGVO: Hinweis am Formular

Ein Kontaktformular erhebt Name, E-Mail und Nachricht. Sagen Sie das neben dem Button und verlinken Sie Ihre Datenschutzerklärung. Der Satz im Beispiel ist ein Anfang. Setzen Sie den Namen des Verantwortlichen und Ihre echte Seite ein.

Für eine Antwort ist eine Pflicht-Checkbox meist das falsche Mittel. Der Hinweis erfüllt die Informationspflicht. Eine Einwilligung, als eigenes nicht angehaktes Kästchen, gehört zur Werbung. Machen Sie das Absenden nicht von diesem Kästchen abhängig.

Das ist allgemeine Information, keine Rechtsberatung. Die ausführliche Vorlage, samt Text für die Datenschutzerklärung, steht im Ratgeber Kontaktformular und DSGVO. Pflichtfelder bleiben die, die Sie für die Antwort brauchen. Mehr abzufragen, nur für den Fall, widerspricht der Datenminimierung.

Wann Formgong passt, und wann nicht

Formgong ist ein kostenloses Formular-Backend für statische und mit KI gebaute Websites. Es liefert Einsendungen an Telegram und per E-Mail, speichert die Daten in der EU und arbeitet in 12 Sprachen.

Im Free-Tarif sind es 300 Einsendungen im Monat, unbegrenzt viele Formulare, ein Empfänger und 30 Tage Aufbewahrung. Telegram kommt sofort. Die E-Mail ist eine Tagesübersicht an diese eine Adresse. Sie geht um 08:00 Uhr in Ihrer Zeitzone raus und fasst den Vortag zusammen. Eine automatische Antwort an den Besucher gibt es im Free-Tarif nicht. Pro und Business mailen jede Einsendung. Pro steht mit 9 US-Dollar im Monat oder 86 US-Dollar im Jahr für 5.000 Einsendungen. Business mit 15 US-Dollar im Monat oder 144 US-Dollar im Jahr für 25.000.

Nach dem Monatslimit liefert ein Puffer von 20 Prozent noch aus. Weitere Anfragen werden gespeichert und nicht gesendet, höchstens noch einmal 300, bis Sie wechseln oder der Monat neu beginnt. Danach werden neue Sendungen abgelehnt.

Nehmen Sie den PHP-Handler, wenn die Anfrage den Host nicht verlassen darf und Sie das Mail-Risiko tragen. Nehmen Sie Contact Form 7, wenn das Formular in wp-admin leben soll und Sie Plugins pflegen. Nehmen Sie den HTML-Block, wenn Sie kein Plugin wollen und mit der Tagesübersicht leben können, oder Telegram nutzen.

Der öffentliche Schlüssel darf im Block stehen. Er kann den Posteingang nicht lesen. Die Tarife stehen unter Preise. Der Speicherort unter Wo Daten liegen.

Häufig gestellte Fragen

Wie erstelle ich ein WordPress-Kontaktformular ohne Plugin?

Setzen Sie das Formular in den Block „Individuelles HTML“, oder verarbeiten Sie die Sendung im Child-Theme. Der Kern hat keinen Kontaktformular-Block. Auf WordPress.com brauchen Formular- und Skript-Tags in diesem Block einen bezahlten Tarif mit aktiven Hosting-Funktionen.

Warum kommt die E-Mail aus dem WordPress-Kontaktformular nicht an?

Gibt wp_mail true zurück, hat PHP die Mail nur angenommen. Das Postfach muss sie nicht bekommen haben. Prüfen Sie den Spam und die Absenderadresse. Auch ein Erfolgstext im Browser ist keine Zustellung. Senden Sie einmal und sehen Sie an dem Ort nach, an den das Formular wirklich sendet.

Wie schütze ich ein WordPress-Kontaktformular vor Spam, ohne Captcha?

Setzen Sie ein leeres Feld namens botcheck, das Menschen nicht sehen und nicht per Tastatur erreichen. Auf dem PHP-Weg leiten Sie bei einem gefüllten Feld still weiter. Bei Formgong wird die Einsendung als Spam abgelegt und nicht zugestellt.

Ist ein WordPress-Kontaktformular ohne Plugin DSGVO-konform?

Das Plugin entscheidet das nicht. Sie brauchen eine Rechtsgrundlage, einen Hinweis am Formular und die Angaben in der Datenschutzerklärung. Eine Pflicht-Checkbox für die Antwort ist meist unnötig. Das ist allgemeine Information, keine Rechtsberatung.

Brauche ich SMTP, wenn WordPress die Mail selbst verschickt?

Ja, wenn nur wp_mail sendet. Ohne passenden Versand über Ihre Domain landet die Mail oft im Spam oder gar nicht. Sendet das Formular an einen Formular-Dienst, verschickt dieser die Nachricht. Ein SMTP-Plugin brauchen Sie dafür nicht.

Quellen und Dokumentation

Offizielle Quellen zu dieser Anleitung: WordPress.com: Eigenes HTML einfügen, WordPress: Rollen und Fähigkeiten, WordPress: wp_mail(), WordPress: wp_kses_allowed_html(), WordPress: Blockliste. Formgong für WordPress, wo die Daten gespeichert werden.

Diesen Artikel als Markdown lesen

Bewerten Sie diesen Leitfaden

Ihre Bewertung

Wir lesen jede Bewertung. Ein Kommentar erscheint hier erst nach unserer Freigabe.

← Zurück zum Blog