Skip to content
Starten Sie mit 100 kostenlosen Verifizierungs-Credits
Qualisend
Alle Artikel
Engineering / 15. Juni 2026

E-Mail-Adressen in Go validieren

7 minutes read

Qualisend team
Ein mehrschichtiges Diagramm einer Go-E-Mail-Adresse, die die Syntaxvalidierung mit net/mail.ParseAddress, eine MX-Abfrage mit net.LookupMX und eine SMTP-Postfachprüfung durchläuft und sich zu einem einzigen Zustellbarkeitsurteil verengt

Eine E-Mail-Adresse in Go zu validieren sind drei Aufgaben, die einen Namen tragen. Die Tutorials, die Ihnen eine Regex zeigen und dann aufhören, haben genau eine davon gelöst – und dazu die am wenigsten nützliche. Echte Validierung ist mehrschichtig: eine günstige Syntaxprüfung, eine DNS-Abfrage und eine SMTP-Postfachprüfung, das Günstigste zuerst. Gos Standardbibliothek deckt die ersten beiden sauber mit net/mail und net ab, ganz ohne Abhängigkeiten; die dritte ist ein Netzwerkproblem, das man besser abgibt. Dieser Leitfaden baut jede Ebene mit funktionierendem, idiomatischem Go auf und zeigt genau, wo eine Verifizierungs-API übernimmt.

Die kurze Antwort#

Verwenden Sie net/mail.ParseAddress für die Syntax, net.LookupMX für die MX-Abfrage und eine Verifizierungs-API für die SMTP-Postfachprüfung – in dieser Reihenfolge, das Günstigste zuerst, mit Kurzschluss, sobald eine Ebene eine Entscheidung liefert. Greifen Sie nicht zu net/smtp, um Postfächer von Ihrem eigenen Server aus zu prüfen: Der ausgehende Port 25 ist auf den meisten Hosts blockiert, und selbst dort, wo er es nicht ist, hängt die Antwort von der Reputation der sendenden IP und vom Greylisting-Verhalten ab, das Sie nicht nachbauen wollen. Jede Ebene sortiert Adressen günstiger aus als die vorherige; nur die letzte kann eine Adresse als gültig bestätigen.

Ebene 1: Syntax mit net/mail#

Go braucht hier keine selbst gebaute Regex. Das net/mail-Paket der Standardbibliothek parst Adressen nach RFC 5322, und ParseAddress gibt bei allem Fehlerhaften einen Fehler ungleich nil zurück – genau das Ja/Nein-Signal, das ein Syntax-Gate braucht:

package main

import "net/mail"

func isValidSyntax(email string) bool {
	if len(email) > 320 {
		return false
	}
	addr, err := mail.ParseAddress(email)
	return err == nil && addr.Address == email
}

Der Vergleich addr.Address == email ist der Teil, den die meisten Beispiele übersehen. ParseAddress ist darauf ausgelegt, eine vollständige Mailbox-Zeile zu lesen, und akzeptiert daher bereitwillig einen Anzeigenamen: mail.ParseAddress("Jane Doe <jane@example.com>") gibt keinen Fehler zurück, wobei addr.Address auf jane@example.com gesetzt ist. Für ein Anmeldefeld wollen Sie die reine Adresse und nichts sonst, also vergleichen Sie die geparste Address mit der Eingabe und weisen Sie alles zurück, was einen Namen, spitze Klammern oder nachgestellte Leerzeichen mitbrachte:

isValidSyntax("jane@example.com")            // true
isValidSyntax("Jane Doe <jane@example.com>") // false — not a bare address
isValidSyntax("not-an-email")                // false — ParseAddress errors
isValidSyntax("a@@b.com")                    // false

Ein Bestehen bedeutet hier „prüfenswert“, nicht „gültig“. Es sagt Ihnen, dass die Zeichenkette wie eine Adresse geformt ist; über die Frage, ob Mail ankommt, sagt es nichts aus. Das ist dieselbe Wand, gegen die jede Regex-basierte E-Mail-Prüfung läuft, und net/mail steht auf derselben Seite davon – es ist ein Parser, kein Zustellbarkeitsorakel. Jede Ebene darunter setzt voraus, dass die Syntax bereits in Ordnung ist.

Ebene 2: Kann die Domain überhaupt Mail empfangen?#

Hier macht sich die Standardbibliothek ohne jede Abhängigkeit bezahlt. Eine Domain, die keine MX-Einträge veröffentlicht, hat keinen Ort, an dem Mail landen kann, sodass eine einzige Abfrage tote Domains, falsch geschriebene Firmennamen und erfundene TLDs eliminiert. net.LookupMX löst sie auf:

package main

import "net"

func hasMailRoute(domain string) bool {
	mxs, err := net.LookupMX(domain)
	if err != nil {
		return false
	}
	return len(mxs) > 0
}
hasMailRoute("gmail.com")               // true
hasMailRoute("company-that-folded.com") // false

LookupMX gibt einen Slice von *net.MX zurück, nach Präferenz sortiert, oder einen *net.DNSError, wenn die Domain nicht aufgelöst werden kann. Jeden Fehler – oder einen leeren Slice – als „keine Mailroute“ zu behandeln, ist die sichere Lesart für einen Validator. Wenn Sie Domains berücksichtigen wollen, die Mail über einen A-Eintrag ohne expliziten MX annehmen (den sogenannten impliziten MX), greifen Sie auf net.LookupHost(domain) zurück, wenn der MX-Slice leer zurückkommt. Für die überwältigende Mehrheit echter Adressen ist eine MX-Prüfung jedoch der richtige Filter.

Eine betriebliche Anmerkung: LookupMX blockiert auf Ihrem DNS-Resolver, sodass ein langsamer oder nicht erreichbarer Nameserver die Goroutine anhält. Verwenden Sie in einem Request-Pfad den kontextbewussten Resolver – (&net.Resolver{}).LookupMX(ctx, domain) – mit einer Deadline, und behandeln Sie einen Timeout als „später prüfen“, nicht als harte Ablehnung, damit ein vorübergehender DNS-Aussetzer nie einen echten Kunden abweist.

Ebene 3: Existiert das Postfach tatsächlich?#

Die Ebenen 1 und 2 können eine Adresse nur aussortieren. Eine Domain kann perfekte MX-Einträge veröffentlichen und dennoch kein Postfach unter der Adresse haben, die Sie in Händen halten – noreply-9f2x@gmail.com ist syntaktisch gültig auf einer aktiven Mailroute und war nie ein echtes Postfach. Ein bestimmtes Postfach zu bestätigen bedeutet die SMTP-Zustellkonversation: zum Mailhost verbinden, RCPT TO absetzen, die Antwort lesen und trennen, bevor irgendetwas gesendet wird.

Go liefert die Werkzeuge, es zu versuchen. net/smtp gibt Ihnen smtp.Dial und einen Client mit den Methoden Mail, Rcpt und Close, und Sie könnten diesen Handshake im Prinzip selbst steuern. In der Praxis sollten Sie ihn nicht von Ihrem Anwendungsserver aus ausführen. Die meisten Cloud-Anbieter blockieren den ausgehenden Port 25 vollständig, sodass der Verbindungsaufbau in der Produktion einfach abläuft, selbst wenn er auf Ihrem Laptop funktioniert hat. Wo der Port offen ist, hängt die Antwort von der Reputation der IP ab, von der aus Sie sich verbinden, und empfangende Server wenden Greylisting und Rate-Limiting auf unbekannte Absender an – sodass eine naive Prüfung zurückgestellt, gedrosselt oder auf Sperrlisten gesetzt wird. Es steckt auch mehr dahinter als ein einziger Roundtrip: Catch-all-Domains akzeptieren jede Adresse und laufen einem einzelnen RCPT TO zuwider. Wie E-Mail-Verifizierung funktioniert geht die gesamte Pipeline durch. Das ist die Ebene, die es wert ist, delegiert zu werden.

Die vollständige Prüfung mit einer API durchführen#

Die Echtzeit-API von Qualisend durchläuft die gesamte Pipeline – Syntax, DNS und die SMTP-Postfachprüfung – auf einer reputationsverwalteten Infrastruktur, die dafür gebaut ist, und liefert ein Urteil zurück. Es ist ein einfacher JSON-POST, sodass net/http und encoding/json aus der Standardbibliothek alles sind, was Sie brauchen. Dekodieren Sie den result-Umschlag direkt in eine Struktur:

package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

type VerifyResult struct {
	Status   string          `json:"status"` // deliverable | risky | undeliverable | unknown
	Score    int             `json:"score"`
	Reason   string          `json:"reason"`
	SubFlags map[string]bool `json:"sub_flags"`
}

type verifyResponse struct {
	Result VerifyResult `json:"result"`
}

func verify(ctx context.Context, email string) (VerifyResult, error) {
	body, err := json.Marshal(map[string]string{"email": email})
	if err != nil {
		return VerifyResult{}, err
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost,
		"https://api.qualisend.com/v1/verify", bytes.NewReader(body))
	if err != nil {
		return VerifyResult{}, err
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("QUALISEND_API_KEY"))
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return VerifyResult{}, err
	}
	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		return VerifyResult{}, fmt.Errorf("qualisend: unexpected status %s", res.Status)
	}

	var payload verifyResponse
	if err := json.NewDecoder(res.Body).Decode(&payload); err != nil {
		return VerifyResult{}, fmt.Errorf("qualisend: decode response: %w", err)
	}
	return payload.Result, nil
}

Der Endpunkt und der Schlüssel oben sind Platzhalter – lesen Sie Ihren gescopten Schlüssel aus der Umgebungsvariablen QUALISEND_API_KEY, anstatt ihn fest einzucodieren, und prüfen Sie die API-Referenz auf der Entwicklerseite für das genaue Format von Request und Response. Das result-Objekt trägt einen status von deliverable, risky, undeliverable oder unknown, einen numerischen score, einen reason und eine sub_flags-Map (Rollenadresse, Wegwerfadresse, Freemail-Anbieter, Catch-all und dergleichen) – die Felder, auf die Sie tatsächlich verzweigen.

Da verify einen context.Context entgegennimmt, besitzt der Aufrufer das Timeout und die Cancellation. Übergeben Sie eine Deadline, damit eine langsame Verifizierung nie einen Anmelde-Request aufhängt:

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

result, err := verify(ctx, "jane@example.com")
if err != nil {
	log.Printf("verify failed: %v", err)
}

Alles zusammenfügen, um eine E-Mail-Adresse in Go zu validieren#

Das Günstigste zuerst, aufhören, sobald Sie eine Antwort haben. Die beiden lokalen Ebenen kosten nichts und weisen den meisten Unrat sofort ab; der API-Aufruf läuft nur auf Adressen, die sie überstanden haben und den Netzwerk- Roundtrip wert sind:

package main

import (
	"context"
	"strings"
)

func validateEmail(ctx context.Context, email string) (VerifyResult, error) {
	if !isValidSyntax(email) {
		return VerifyResult{Status: "undeliverable", Reason: "invalid_email"}, nil
	}

	domain := email[strings.LastIndex(email, "@")+1:]
	if !hasMailRoute(domain) {
		return VerifyResult{Status: "undeliverable", Reason: "invalid_domain"}, nil
	}

	return verify(ctx, email)
}

Verzweigen Sie dann auf den Status, so wie Ihr Produkt es braucht – die Unzustellbaren abweisen, die Zustellbaren durchlassen und pro Feature entscheiden, was mit den Urteilen in der Grauzone geschehen soll:

result, err := validateEmail(ctx, "jane@example.com")
if err != nil {
	// network/API failure — fail open or retry, don't block a real user
}

switch result.Status {
case "deliverable":
	// accept
case "undeliverable":
	// reject at the form
default:
	// risky | unknown — flag for review, or allow with a soft warning
}

Diese Reihenfolge ist der ganze Trick, und es ist dieselbe Form, die Sie in den Node.js- und Python-Versionen dieses Leitfadens finden – die Schichtung, nicht die Sprache, macht die Validierung zuverlässig. Siehe wie E-Mail-Verifizierung funktioniert dafür, warum jede Stufe dort sitzt, wo sie sitzt.

Häufig gestellte Fragen#

Reicht net/mail.ParseAddress aus, um eine E-Mail-Adresse in Go zu validieren?#

Für die Syntax: ja – net/mail.ParseAddress ist die richtige Prüfung auf Ebene eins und eine weitaus bessere Wahl als eine selbst gebaute Regex, da es gegen RFC 5322 parst und bei fehlerhafter Eingabe einen Fehler zurückgibt. Aber es validiert die Form, nicht die Zustellbarkeit: Es löst niemals DNS auf und kontaktiert keinen Mailserver, sodass ein nil-Fehler „sieht aus wie eine Adresse“ bedeutet, nicht „wird zugestellt“. Vergleichen Sie addr.Address mit Ihrer Eingabe, um Anzeigenamen abzuweisen, und kombinieren Sie die Prüfung dann mit einer MX-Abfrage und einer SMTP-Postfachprüfung.

Wie frage ich MX-Einträge in Go ab?#

Verwenden Sie net.LookupMX(domain) aus der Standardbibliothek. Es gibt einen Slice von *net.MX-Einträgen zurück, nach Präferenz sortiert, oder einen *net.DNSError, wenn die Domain nicht aufgelöst werden kann. Behandeln Sie einen Fehler oder einen leeren Slice als „keine Mailroute“, und verwenden Sie in einem Request-Pfad das kontextbewusste (*net.Resolver).LookupMX(ctx, domain), damit ein langsamer Nameserver die Goroutine nicht über Ihre Deadline hinaus blockieren kann.

Kann ich ein Postfach in Go ohne externen Dienst verifizieren?#

Teilweise. net.LookupMX bestätigt, dass die Domain Mail annimmt, was tote Domains kostenlos aussortiert und nichts über die Standardbibliothek hinaus benötigt. Das Postfach selbst zu bestätigen erfordert eine SMTP-Konversation, die Sie mit net/smtp skripten können, aber nicht von Ihrem App-Server aus ausführen sollten – Port 25 ist weithin blockiert und das Ergebnis hängt von der Reputation Ihrer sendenden IP ab. Genau diese Ebene übernimmt ein Verifizierungsdienst; den Unterschied sehen Sie bei jeder Adresse mit dem kostenlosen E-Mail-Checker.

Sollte ich E-Mails bei der Anmeldung oder beim Bereinigen einer Liste validieren?#

Beides, in unterschiedlicher Tiefe. Führen Sie die Syntaxprüfung und die MX-Abfrage bei der Anmeldung synchron aus – sie sind schnell genug, um den Request zu blockieren und sofortiges Feedback zu geben – und handeln Sie auch dort nach dem unmittelbaren API-Urteil. Reservieren Sie die tiefere, SMTP-bestätigte Arbeit für die Listenbereinigung und Backoffice-Jobs; der Leitfaden zur serverlosen Anmeldung zeigt das Echtzeitmuster von Anfang bis Ende.


Bereit, die SMTP-Ebene zu Ihrem Go-Dienst hinzuzufügen? Die Qualisend-API-Referenz enthält den /verify-Endpunkt mit gescopten Schlüsseln und Copy-and-paste-Beispielen, und der kostenlose E-Mail-Checker lässt Sie zusehen, wie eine syntaktisch perfekte Adresse als undeliverable zurückkommt, bevor Sie eine Zeile Code schreiben.

Your reputation, protected.

Clean your first list in minutes. 100 free credits, no card required.

Get started