Email deliverability checker.
Check SPF, DKIM, DMARC and MX for a domain in one pass. Everything a receiving mail server looks at before deciding whether your message is real: who may send for the domain, whether messages are signed, what to do when they are not, and whether the mail servers themselves speak TLS. Each finding is explained, not just flagged.
Checked example.com via 1.1.1.1 · 31ms JSON · text
Findings
-
info
MX records null MX
The domain publishes a null MX (RFC 7505): it deliberately does not accept mail. That is the right way to say so.
-
pass
SPF -all, 0 of 10 lookups
Hard fail for anyone not listed. This is the strongest setting.
-
warn
DMARC p=reject, no rua
Enforcing, but no aggregate report address. You will not find out when a legitimate sender breaks or when someone starts spoofing you. Add rua=mailto:...
Why this matters: DMARC p=none vs quarantine vs reject: which policy to use and when to move
-
pass
DKIM amazonses, cm, default, dkim, everlytickey1, everlytickey2, fm1, fm2, fm3, google, hs1, hs2, k1, k2, k3, krs, litesrv, m1, mail, mailjet, mandrill, mimecast, mte1, mte2, mxvault, pm, protonmail, protonmail2, protonmail3, s1, s2, s3, selector1, selector2, sendgrid, sg, sig1, smtp, sparkpost, x, zendesk1, zendesk2, zoho
Public keys found for these selectors, all 2048 bits or stronger.
-
info
MTA-STS not published
Optional. MTA-STS tells sending servers to require TLS and a valid certificate when delivering to you, closing the downgrade gap in opportunistic STARTTLS. Gmail and Outlook honour it.
Why this matters: MTA-STS explained: forcing TLS on inbound mail without breaking delivery
-
info
TLS-RPT not published
Optional. TLS-RPT gives you daily reports from big senders about TLS failures when delivering to your domain. Cheap to add and very useful alongside MTA-STS.
Why this matters: MTA-STS explained: forcing TLS on inbound mail without breaking delivery
SPF tree 0 of 10 DNS lookups used · 0 of 2 void
- -all hard fail: anything else is rejected
DMARC _dmarc.example.com
v=DMARC1;p=reject;sp=reject;adkim=s;aspf=s
DKIM keys found by probing common selectors
| Selector | Key | Flags | Record |
|---|---|---|---|
| amazonses | revoked (empty p=) | v=DKIM1; p= | |
| cm | revoked (empty p=) | v=DKIM1; p= | |
| default | revoked (empty p=) | v=DKIM1; p= | |
| dkim | revoked (empty p=) | v=DKIM1; p= | |
| everlytickey1 | revoked (empty p=) | v=DKIM1; p= | |
| everlytickey2 | revoked (empty p=) | v=DKIM1; p= | |
| fm1 | revoked (empty p=) | v=DKIM1; p= | |
| fm2 | revoked (empty p=) | v=DKIM1; p= | |
| fm3 | revoked (empty p=) | v=DKIM1; p= | |
| revoked (empty p=) | v=DKIM1; p= | ||
| hs1 | revoked (empty p=) | v=DKIM1; p= | |
| hs2 | revoked (empty p=) | v=DKIM1; p= | |
| k1 | revoked (empty p=) | v=DKIM1; p= | |
| k2 | revoked (empty p=) | v=DKIM1; p= | |
| k3 | revoked (empty p=) | v=DKIM1; p= | |
| krs | revoked (empty p=) | v=DKIM1; p= | |
| litesrv | revoked (empty p=) | v=DKIM1; p= | |
| m1 | revoked (empty p=) | v=DKIM1; p= | |
| revoked (empty p=) | v=DKIM1; p= | ||
| mailjet | revoked (empty p=) | v=DKIM1; p= | |
| mandrill | revoked (empty p=) | v=DKIM1; p= | |
| mimecast | revoked (empty p=) | v=DKIM1; p= | |
| mte1 | revoked (empty p=) | v=DKIM1; p= | |
| mte2 | revoked (empty p=) | v=DKIM1; p= | |
| mxvault | revoked (empty p=) | v=DKIM1; p= | |
| pm | revoked (empty p=) | v=DKIM1; p= | |
| protonmail | revoked (empty p=) | v=DKIM1; p= | |
| protonmail2 | revoked (empty p=) | v=DKIM1; p= | |
| protonmail3 | revoked (empty p=) | v=DKIM1; p= | |
| s1 | revoked (empty p=) | v=DKIM1; p= | |
| s2 | revoked (empty p=) | v=DKIM1; p= | |
| s3 | revoked (empty p=) | v=DKIM1; p= | |
| selector1 | revoked (empty p=) | v=DKIM1; p= | |
| selector2 | revoked (empty p=) | v=DKIM1; p= | |
| sendgrid | revoked (empty p=) | v=DKIM1; p= | |
| sg | revoked (empty p=) | v=DKIM1; p= | |
| sig1 | revoked (empty p=) | v=DKIM1; p= | |
| smtp | revoked (empty p=) | v=DKIM1; p= | |
| sparkpost | revoked (empty p=) | v=DKIM1; p= | |
| x | revoked (empty p=) | v=DKIM1; p= | |
| zendesk1 | revoked (empty p=) | v=DKIM1; p= | |
| zendesk2 | revoked (empty p=) | v=DKIM1; p= | |
| zoho | revoked (empty p=) | v=DKIM1; p= |
About this tool
Every check is a live DNS query from this server (via Cloudflare's resolver) plus a short connection to each mail server on port 25 to see whether it offers STARTTLS and what certificate it presents. Nothing is sent and no mail is involved; the SMTP session stops after EHLO and STARTTLS. Only public domains can be checked and nothing is stored. For the email findings alongside DNS, certificate and HTTP in one graded report, run the domain health checker.
MX lookup and STARTTLS
The mail servers for the domain in preference order, the addresses they resolve to, and whether each answers on port 25. For each one that does, the probe sends EHLO, asks for STARTTLS and records the TLS version negotiated and whether the certificate is valid for the MX hostname. A refused STARTTLS means mail to you crosses the internet in the clear; a certificate for the wrong name means senders that enforce MTA-STS will not deliver. A domain that publishes a single null MX (0 .) is saying it does not receive mail, and is graded on that basis.
SPF checker
The v=spf1 record is parsed into a tree so you can see what every include pulls in, with the DNS-querying mechanisms (include, a, mx, ptr, exists, redirect) counted against the limit of ten exactly as a receiver counts them, and the all qualifier at the end. The count is the thing to watch: one SaaS provider's include can silently bring three or four lookups with it, and at eleven the whole record is a PermError and receivers ignore it. Two SPF records is the other way to get there. See the SPF 10 lookup limit and SPF PermError.
Three things the tree shows that a plain count misses. An include reached twice through two different providers is counted twice, because receivers evaluate it twice; the second copy is marked. A lookup that returns nothing (an include of a name that does not exist or has no TXT, an mx term on a domain with no MX) is a void lookup, and RFC 7208 allows two of those before the record is a PermError in its own right. And when an include target is a CNAME, the chain is shown, so a record that "vanished" because the provider aliased the name to something with no TXT is explained rather than just red. Each mx term is resolved to count its hosts against the separate limit of ten, a redirect that sits next to an all is marked as ignored, and unknown mechanisms and malformed ip4/ip6 terms are called out as the PermError they are. The JSON output carries all of this: lookups, void_lookups, via_cname, duplicate and a flat spf_problems list.
DKIM checker
Public keys found under common selectors (google, selector1, selector2, k1, default and others), the key type and length, and flags such as testing mode (t=y) or a revoked key (empty p=). Selectors are chosen by the sender, so a custom one cannot be guessed here; find it in the DKIM-Signature header of a message the service sent (s=) and check it with a DNS TXT lookup on selector._domainkey.domain. Keys under 2048 bits still verify but are increasingly flagged.
DMARC checker
The _dmarc record decoded: the policy (p=), the subdomain policy, the percentage it applies to, the SPF and DKIM alignment modes and where aggregate and forensic reports go. Policy is what receivers act on: none monitors, quarantine sends failures to spam, reject refuses them. A record with p=none and no rua address is collecting nothing and enforcing nothing. See DMARC none vs quarantine vs reject and, for how the three mechanisms depend on each other, SPF vs DKIM vs DMARC.
MTA-STS, TLS-RPT and BIMI
Whether the domain tells large senders to insist on TLS: the _mta-sts record and the policy file at https://mta-sts.domain/.well-known/mta-sts.txt, fetched the way a sender fetches it (no redirects followed) and checked line by line: version, mode, max_age, the mx patterns, the id format and the content type. The mx lines are then matched against the real MX hosts with the RFC 8461 wildcard rule, and each host is shown as covered or not. In enforce mode an uncovered host, a host that refuses STARTTLS, or a certificate that does not verify for the MX name is graded as a fail, because that is what Gmail and Outlook.com do with it: they stop delivering to that host. max_age is written out as a duration, since it is the length of time a sender keeps applying the policy it has cached if the id is not changed or the policy host is down, which is how an MX migration turns into a week of bounces. See MTA-STS explained and max_age and the policy cache. The block also shows where TLS failure reports are sent (_smtp._tls), and whether a brand logo record is published and the DMARC policy is strict enough for it to be honoured.
How SPF, DKIM and DMARC fit together
SPF says which servers may send mail for the domain. Over ten DNS lookups and the whole record is ignored, which is the most common reason SPF "stops working" after adding a new SaaS provider. DKIM signs each message with a private key whose public half is published under a selector; the selector name is chosen by the sender. DMARC ties the two together: it tells receivers what to do when a message fails both and where to send reports. Without DMARC, SPF and DKIM are advisory. MTA-STS and TLS-RPT harden the connection itself: STARTTLS on its own can be stripped by anyone in the path, and MTA-STS lets you tell big senders to refuse unencrypted delivery.
Common email deliverability problems
- Mail landing in spam: usually no DMARC, or DKIM not set up for one of the sending services, so messages from that service fail alignment.
- SPF permerror: more than ten lookups, or two SPF records. Either one makes receivers ignore SPF entirely.
- SPF count higher than the record looks: two providers both include the same upstream record (Microsoft 365 and a helpdesk tool that relays through it, say), and receivers count it both times. The tree marks the second copy.
- SPF include of a dead provider: an include whose target no longer has an SPF record is a PermError on its own, and each one is also a void lookup. Remove it.
- SPF ends in +all: anyone may send as you. Treated as no SPF by many receivers.
- DMARC stuck at p=none: reports arrive but nothing is enforced, so spoofing still gets through. Move to quarantine once the reports are clean.
- DKIM key under 2048 bits: 1024-bit RSA keys are still accepted but increasingly flagged; rotate to 2048.
- MX with no STARTTLS or a bad certificate: Gmail and Outlook will still deliver, but show a warning, and MTA-STS enforcing senders will not.
- MTA-STS policy that does not name the current MX hosts: the MX records were changed and the policy was not, or was changed without a new
id. Enforcing senders keep applying the cached policy and refuse the new hosts untilmax_ageruns out.
Examples
- example.com: a domain that sends no mail
- gmail.com: a large provider with everything set
- github.com: SPF includes from several services
From the command line
curl "kirkdiamond.com/tools/mail?domain=example.com" or add &format=json. Useful for working out why mail lands in spam, checking a new provider's records before switching, confirming the last step to p=reject is safe, and auditing a domain you are about to send on behalf of.
Every parameter, the JSON field names, rate limits and ready-made CI checks are in the API and curl reference.