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

E-Mail-Adressen in Rails validieren

8 minutes read

Qualisend team
Ein Code-Editor-Fenster mit dem Titel user.rb, einer roten Rails-Spur und einem grünen Zustellbarkeits-Pill, darüber die Bildunterschrift validates zu MX zu Postfach.

In dem Moment, in dem Sie in Rails eine E-Mail-Adresse validieren müssen, lässt Active Record es trivial erscheinen — schreiben Sie validates :email, sehen Sie den Test bestehen, machen Sie weiter. Doch diese eine Zeile beantwortet nur die erste der drei Fragen, die echte Validierung stellt. Ist die Adresse korrekt geformt? Kann ihre Domain überhaupt Mail empfangen? Existiert das Postfach tatsächlich? Rails liefert eine saubere Antwort auf die erste Frage und gar nichts für die anderen beiden. Dieser Leitfaden baut jede Ebene als idiomatische Rails-Validierung auf — eine format-Regel, dann zwei eigene ActiveModel::EachValidator-Klassen — und zeigt genau, wo das Framework aufhört. Er erweitert den Leitfaden zur E-Mail-Validierung in Ruby: Rails verpackt dieselben Bausteine der Standardbibliothek in die deklarative Validierungs-API.

Die Kurzfassung#

Verwenden Sie validates :email, format: { with: URI::MailTo::EMAIL_REGEXP } für die Syntax, einen eigenen Validator auf Basis der Standardbibliothek resolv für die MX-Abfrage und eine Verifizierungs-API für die SMTP-Postfachprüfung — drei Validierungen auf einem Attribut, das günstigste zuerst, wobei jede übersprungen wird, wenn eine günstigere Ebene bereits fehlgeschlagen ist. Öffnen Sie keine SMTP-Sitzung aus Ihrer App heraus, um Postfächer selbst zu sondieren: Der ausgehende Port 25 ist auf den meisten Hosts blockiert, und die Antwort hängt von der Reputation der sendenden IP sowie von Greylisting ab, das Sie nicht neu implementieren möchten. Eine Besonderheit unterscheidet Rails von Frameworks mit einem bail-Schlüsselwort — Active Model führt jeden deklarierten Validator aus und sammelt alle Fehler, sodass ein Kurzschluss eine Schutzabfrage ist, die Sie hinzufügen, kein Flag, das Sie übergeben. Jede Ebene schließt Adressen günstiger aus als die vorherige; nur die letzte kann eine Adresse einschließen.

Ebene 1: Formatvalidierung mit einer validates-Regel#

Rails gehört Ebene eins, und Sie müssen keine Regex schreiben, um sie einzufordern. Rubys uri-Bibliothek — die Rails ohnehin lädt — liefert ein gut getestetes Muster in URI::MailTo::EMAIL_REGEXP, übergeben Sie es also einer format-Validierung, statt etwas von Stack Overflow einzufügen:

app/models/user.rb

class User < ApplicationRecord
  validates :email,
            presence: true,
            format: { with: URI::MailTo::EMAIL_REGEXP }
end

Diese Konstante ist dieselbe, auf die sich der Ruby-Leitfaden stützt. Sie ist bereits mit \A und \z verankert, prüft also den gesamten String, und sie ist bewusst permissiv: Sie folgt der WHATWG/HTML5-Definition, die lockerer ist als RFC 5322 und bereitwillig jane@localhost akzeptiert, weil sie keinen Punkt in der Domain verlangt. Das ist ein Feature. Die Aufgabe von Ebene eins ist es, Tippfehler bei der Eingabe abzufangen, nicht die RFCs auszufechten — was ohnehin nicht helfen würde.

Was zählt, ist zu wissen, wo Rails aufhört. Jede integrierte E-Mail-Funktion — die format-Regel, die Scaffolds, der email_field-Helper — validiert die Form. Keine davon löst DNS auf oder kontaktiert einen Mailserver, sodass noreply-9f2x@gmail.com, typo@gmial.com und sales@company-that-folded.com alle bestehen. Alle drei sind unzustellbar. Rails liefert überhaupt keine Zustellbarkeitsprüfung mit.

Ebene 2: Kann die Domain Mail empfangen?#

Dies ist die erste Ebene, die Rails Ihnen nicht abnimmt, und sie lässt sich günstig nachrüsten. Eine Domain ohne Mail-Route kann für niemanden Mail annehmen, sodass eine einzige MX-Abfrage tote Domains, falsch geschriebene Firmennamen und erfundene TLDs ausschließt. Ein Rails-Validator ist einfach eine Klasse, die von ActiveModel::EachValidator ableitet und validate_each implementiert — verpacken Sie also die MX-Abfrage der resolv-Bibliothek in einen solchen:

app/validators/deliverable_domain_validator.rb

require "resolv"

class DeliverableDomainValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    return if value.blank? || record.errors[attribute].present?

    domain = value.rpartition("@").last
    return if domain.present? && mail_route?(domain)

    record.errors.add(attribute, options[:message] || "can't receive email")
  end

  private

  def mail_route?(domain)
    records = Resolv::DNS.open do |dns|
      dns.getresources(domain, Resolv::DNS::Resource::IN::MX)
    end
    !records.empty?
  rescue Resolv::ResolvError, Resolv::ResolvTimeout
    false
  end
end

Resolv::DNS.open gibt Ihnen einen Resolver und schließt ihn, wenn der Block endet, und getresources liefert ein Array von MX-Einträgen zurück — leer, wenn die Domain keine veröffentlicht oder nicht existiert, was genau der Fall "keine Route" ist. Das rescue deckt den anderen Fehlermodus ab; beachten Sie, dass Resolv::ResolvTimeout keine Unterklasse von Resolv::ResolvError ist, benennen Sie also beide, wenn ein langsamer Nameserver sauber durchfallen soll. Binden Sie ihn über seinen abgeleiteten Schlüssel in das Modell ein — Rails camelisiert deliverable_domain zu DeliverableDomainValidator und findet die Klasse, die Zeitwerk aus app/validators automatisch geladen hat:

validates :email,
          presence: true,
          format: { with: URI::MailTo::EMAIL_REGEXP },
          deliverable_domain: true

Die Schutzabfrage return if ... record.errors[attribute].present? ist der ganze Trick. Active Model führt jeden deklarierten Validator aus und aggregiert die Fehler — es gibt kein bail. Doch Validierungen laufen in der Reihenfolge, in der Sie sie deklarieren, sodass zu dem Zeitpunkt, an dem diese auslöst, ein format-Fehler bereits auf record.errors[:email] sitzt und die Schutzabfrage die DNS-Abfrage vollständig überspringt. Eine fehlerhaft geformte Adresse verbraucht niemals einen Nameserver-Roundtrip.

Ebene 3: Existiert das Postfach tatsächlich?#

Ebene eins und zwei können eine Adresse nur ausschließen. Eine Domain kann perfekte MX-Einträge veröffentlichen und trotzdem kein Postfach an der Adresse haben, die Sie in der Hand halten — noreply-9f2x@gmail.com ist syntaktisch gültig auf einer aktiven Mail-Route und dennoch ein Postfach, das nie angelegt wurde. Ein bestimmtes Postfach zu bestätigen bedeutet die SMTP-Zustellungskonversation: sich mit dem Mail-Host verbinden, RCPT TO absetzen, die Antwort lesen und trennen, bevor irgendetwas gesendet wird. Es steckt mehr dahinter als das — wie E-Mail-Verifizierung funktioniert durchläuft die gesamte Pipeline, einschließlich Catch-all-Domains, die jede Adresse annehmen und eine naive Sondierung aushebeln.

Rubys net/smtp öffnet diese Konversation bereitwillig. Das Problem ist nicht der Code; es ist das Netzwerk. Führen Sie eine SMTP-Sondierung von Ihrem Anwendungsserver aus, und drei Dinge gehen schief: Die meisten Cloud-Anbieter blockieren den ausgehenden Port 25 vollständig, die Antwort hängt von der Reputation der IP ab, von der aus Sie sich verbinden, und empfangende Server greylisten und drosseln unbekannte Absender — sodass eine Sondierung, die auf Ihrem Laptop funktioniert, in der Produktion still fehlschlägt oder Sie auf eine Blockliste bringt. Dies ist die Ebene, die es sich zu delegieren lohnt.

Qualisend aus einem eigenen Validator aufrufen#

Die Delegation bleibt sauber, weil eine Verifizierungs-API nur ein weiterer Validator ist. Der verify-Endpunkt von Qualisend durchläuft die gesamte Pipeline — Syntax, DNS und die SMTP-Postfachsondierung — von einer reputationsverwalteten Infrastruktur aus und liefert ein einziges Urteil zurück. Es ist ein einfacher JSON-POST, sodass die Standardbibliothek net/http ihn ohne Gems abdeckt. Lesen Sie den Schlüssel aus der Umgebung, statt ihn fest zu verdrahten:

app/validators/deliverable_email_validator.rb

require "net/http"
require "json"
require "uri"

class DeliverableEmailValidator < ActiveModel::EachValidator
  VERIFY_URL = URI("https://api.qualisend.com/v1/verify")

  def validate_each(record, attribute, value)
    return if value.blank? || record.errors[attribute].present?

    result = verify(value)
    return if result.nil? # our outage shouldn't block a real signup

    if result["status"] == "undeliverable"
      record.errors.add(attribute, options[:message] || "appears to be undeliverable")
    end
  end

  private

  def verify(email)
    request = Net::HTTP::Post.new(VERIFY_URL)
    request["Authorization"] = "Bearer #{ENV.fetch('QUALISEND_API_KEY')}"
    request["Content-Type"]  = "application/json"
    request.body = JSON.generate(email: email)

    response = Net::HTTP.start(
      VERIFY_URL.hostname, VERIFY_URL.port,
      use_ssl: true, open_timeout: 5, read_timeout: 10
    ) { |http| http.request(request) }

    return nil unless response.is_a?(Net::HTTPSuccess)

    JSON.parse(response.body)["result"]
  rescue StandardError
    nil # unreachable or slow — treat as unknown and re-verify later
  end
end

Dieser Endpunkt ist ein Platzhalter — prüfen Sie die API-Referenz für die genaue Basis-URL, die Request-Struktur und wie Sie einen scoped Key erstellen. Die Antwort umschließt ein result-Objekt:

{
  "result": {
    "status": "deliverable",
    "score": 95,
    "reason": null,
    "sub_flags": { "role": false, "disposable": false, "catch_all": false }
  }
}

status ist einer von deliverable, risky, undeliverable oder unknown. Der obige Validator lässt nur undeliverable hart durchfallen und lässt risky und unknown passieren, sodass Sie nachgelagert entscheiden können, was damit geschehen soll — auf score filtern oder auf einen sub_flags-Eintrag wie disposable verzweigen — statt eine Grenzfall-Adresse in einen Formularfehler zu verwandeln. Und wenn der Request selbst fehlschlägt, gibt verify nil zurück und der Validator fügt keinen Fehler hinzu: Die Syntax- und MX-Ebenen liefen bereits lokal, sodass ein Abmildern nur der Netzwerkebene verhindert, dass ein vorübergehender Ausfall eine echte Registrierung blockiert. Behandeln Sie das obige JSON als die Struktur, nicht als den Vertrag; die vollständige Feldliste finden Sie in den Entwicklerdokumenten.

Die Ebenen zusammenführen, um eine E-Mail-Adresse in Rails zu validieren#

Deklarieren Sie alle drei Validierungen auf dem Attribut, das günstigste zuerst. Die Deklarationsreihenfolge ist die Ausführungsreihenfolge, und jede Schutzabfrage überspringt, wenn eine günstigere Ebene bereits fehlgeschlagen ist, sodass sich die zusammengesetzte Regel von oben nach unten genau so liest, wie sie ausgeführt wird:

app/models/user.rb

class User < ApplicationRecord
  validates :email,
            presence: true,
            format: { with: URI::MailTo::EMAIL_REGEXP },
            deliverable_domain: true,
            deliverable_email: true
end

Nun erreicht user.save die API nur bei Adressen, die Syntax und MX-Prüfung bereits bestanden haben — ein Tippfehler scheitert bei format und verbraucht niemals einen Nameserver-Roundtrip oder ein API-Guthaben, und eine tote Domain scheitert bei deliverable_domain, bevor der Netzwerkaufruf erfolgt. user.valid? führt die gesamte Kette aus, und user.errors[:email] trägt, welche Ebene auch immer Einspruch erhoben hat. Diese Reihenfolge, nicht das Framework, macht die Validierung zuverlässig — dieselbe Struktur wie die Ruby-Version, ausgedrückt als deklarative Validierungen statt als handverkettete Methode.

Weil dies gewöhnliche Validierungen sind, funktioniert jede Standardoption. Beschränken Sie die beiden Netzwerkebenen mit if: :email_changed?, damit ein unabhängiges user.update(name: ...) keine DNS-Abfrage erneut ausführt und kein Guthaben für eine bereits verifizierte Adresse erneut verbraucht. Halten Sie den synchronen Pfad auf die schnellen lokalen Ebenen plus das unmittelbare API-Urteil bei der Registrierung; alles Aufwändigere gehört in einen Background-Job. Der Leitfaden zur serverlosen Registrierung zeigt das Echtzeit-Muster, und wie man eine E-Mail-Liste bereinigt deckt die Batch-Seite ab.

Häufig gestellte Fragen#

Bietet Rails eine integrierte Möglichkeit zu prüfen, ob eine E-Mail zustellbar ist?#

Nein. Jede integrierte E-Mail-Funktion — die format-Validierung mit URI::MailTo::EMAIL_REGEXP, die Scaffolds, der email_field-Helper — validiert nur die Form. Keine davon löst DNS auf oder kontaktiert einen Mailserver, sodass eine erfundene Domain oder ein nicht existierendes Postfach die Prüfung besteht. Ergänzen Sie eine MX-Abfrage und eine SMTP-Postfachprüfung als eigene Validatoren, um die Zustellbarkeit abzudecken, denn Rails liefert beides nicht mit.

Wie schreibe ich einen eigenen E-Mail-Validator in Rails?#

Leiten Sie von ActiveModel::EachValidator ab und implementieren Sie validate_each(record, attribute, value), wobei Sie record.errors.add(attribute, message) aufrufen, wenn der Wert die Prüfung nicht besteht. Speichern Sie die Klasse unter app/validators, damit Zeitwerk sie automatisch lädt, und binden Sie sie dann über ihren abgeleiteten Schlüssel ein — ein DeliverableDomainValidator wird als deliverable_domain: true eingebunden. Er akzeptiert dieselben Optionen wie jede Validierung, sodass if:, on: und ein eigenes message: unverändert funktionieren.

Kann ich in Rails ohne API prüfen, ob ein Postfach existiert?#

Teilweise. Die Standardbibliothek resolv bestätigt, dass die Domain Mail annimmt, was tote Domains kostenlos und direkt aus einem eigenen Validator heraus ausschließt. Das Postfach zu bestätigen bedeutet eine SMTP-Konversation, die Sie mit net/smtp öffnen können, aber nicht von Ihrem App-Server aus ausführen sollten — der ausgehende Port 25 ist vielerorts blockiert und das Ergebnis hängt von der Reputation Ihrer sendenden IP ab. Genau diese letzte Ebene übernimmt ein Verifizierungsdienst von einer eigens dafür reputationsverwalteten Infrastruktur aus.

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

Beides, in unterschiedlicher Tiefe. Führen Sie die Syntax- und MX-Ebenen bei der Registrierung synchron aus — sie sind schnell genug, um den Request zu blockieren und sofortiges Feedback zu geben — und handeln Sie dort auch nach dem unmittelbaren API-Urteil. Reservieren Sie tiefere, gebündelte Verifizierung für das Bereinigen einer bestehenden Liste, wo Latenz keine Rolle spielt, Gründlichkeit aber schon. Wenn Sie die Netzwerk-Validatoren auf if: :email_changed? beschränken, verhindern Sie, dass gewöhnliche Aktualisierungen eine bereits geprüfte Adresse erneut verifizieren.


Bereit, die Postfach-Ebene hinzuzufügen? Die API-Referenz enthält den /verify-Endpunkt, scoped Keys und Copy-and-paste-Snippets, oder fügen Sie eine Adresse in den kostenlosen E-Mail-Checker ein und sehen Sie zu, wie ein syntaktisch perfekter String als unzustellbar zurückkommt.

Your reputation, protected.

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

Get started