Zurück zur Übersicht

Hermes: Eine E-Mail-Validierungs-API

Was es braucht, eine E-Mail-Adresse wirklich zu prüfen – von Syntax über MX-Lookup bis SMTP. Die Idee hinter Hermes, der Tech-Stack und was ich beim Bauen gelernt habe.

Blau-schwarzes Stahlgehäuse
Foto auf Unsplash

Hermes ist meine E-Mail-Validierungs-API. Man schickt eine Adresse an einen REST-Endpoint und bekommt zurück, ob sie wirklich zustellbar ist. Hier kurz, was dahintersteckt.

Die Idee kam an einem Abend: Was passiert eigentlich, wenn man prüft, ob eine E-Mail-Adresse existiert? Wer mal einen Newsletter verschickt oder ein Registrierungsformular gebaut hat, kennt das Problem – ungültige Adressen verursachen Bounces, ruinieren die Sender-Reputation und verfälschen Nutzerzahlen. Der Name ist eine Anlehnung an den griechischen Götterboten: Hermes soll dafür sorgen, dass Nachrichten ankommen.

Kein SDK, kein kompliziertes Setup – einfach ein POST /api/v1/validate und eine klare Antwort. Entwickler als Zielgruppe.

Was Hermes prüft

Ein Regex reicht bei weitem nicht. Hermes führt bis zu 13 Checks pro Adresse durch, unter anderem:

  • Syntax – RFC-konform, nicht nur ein Einzeiler
  • MX-Record – Existiert ein Mailserver für die Domain?
  • SMTP – Antwortet der Server auf ein RCPT TO, ohne dass eine Mail gesendet wird?
  • Catch-All – Akzeptiert die Domain jede Adresse?
  • Wegwerf-Adressen – Abgleich gegen tausende Disposable-Domains
  • Tippfehlergmial.com → meintest du gmail.com?
  • SPF & DMARC, Rollen-Adressen (info@), kostenlose Anbieter, High-Risk-TLDs …
  • Deliverability-Score – am Ende ein Wert zwischen 0 und 1

Der spannendste Teil ist SMTP: TCP-Verbindung zum Mailserver öffnen, das Protokoll sprechen und fragen, ob die Mailbox existiert. Funktioniert erstaunlich gut – bis auf Catch-All-Server, die zu jeder Adresse „ja“ sagen. Das korrekt einzuordnen war die meiste Arbeit.

Der Aufbau

Jeder Check ist ein eigenes Objekt hinter einem Contract, ein EmailValidator orchestriert die Reihenfolge:

interface Check
{
    public function run(ValidationResult $result): void;
}

Ist die Syntax kaputt, spare ich mir den DNS-Lookup. Sagt der Server „nein“, fällt der Score. DNS und SMTP stecken hinter eigenen Interfaces – im Test schiebe ich Fakes rein und prüfe die ganze Logik, ohne ein Paket übers Netz zu schicken.

Feature-Gating läuft nach demselben Prinzip: intern werden immer alle Checks gemacht, erst am Ende filtert ein PlanGate alles raus, was der Plan nicht enthält. Die Validierungslogik weiß so nichts von Plänen, und ein Upgrade wirkt sofort.

Der Stack: Laravel auf PHP 8.5, Inertia + React fürs Dashboard, Fortify für Auth, ein API-Key-Header für die öffentliche API, Pest für die Tests. (Die erste Version lief auf Next.js – Laravel war am Ende einfach das, womit ich am schnellsten baue.)

Was ich gelernt habe

DNS ist wilder als gedacht. Manche Domains haben keinen MX-Record, nehmen Mail aber über den A-Record an. Manche SPF-Records sind syntaktisch kaputt, werden von den großen Providern aber trotzdem akzeptiert. Ein jahrzehntealter Flickenteppich.

Rate-Limiting ist nicht trivial. Ein Token-Bucket pro API-Key auf dem Cache-Backend skaliert über mehrere Server – aber echte Atomarität bei Batch-Requests kostet extra. Für eine Validierungs-API reicht ein kleiner Overshoot als Worst Case.

Bestehende Dienste sind oft teuer oder intransparent. Komplizierte SDKs, verschachteltes Pricing, kaum Einblick in das, was sie eigentlich prüfen. Ich wollte etwas Entwicklerfreundliches, bei dem man genau sieht, was geprüft wurde und warum.


Hermes ist noch Work in Progress. Die Kernlogik steht, das Dashboard läuft, die API antwortet. Wer es vorab testen will, meldet sich gerne.

Mehr dazu unter hermes.leutsch.dev.

Ähnliche Beiträge

Was es braucht, eine E-Mail-Adresse wirklich zu prüfen – von Syntax über MX-Lookup bis SMTP. Die Idee hinter Hermes, der Tech-Stack und was ich beim Bauen gelernt habe.

Vor Kurzem haben wir ein Paket ersetzt, das es uns erlaubte, einen Model-Datensatz samt aller Beziehungen zu duplizieren und anschließend Änderungen am neuen Record vorzunehmen.