The commands

Five lookups tell you nearly everything about a domain’s mail setup. Run them against the domain in the From: address, not your website’s hostname if those differ.

dig +short MX example.com                     # where incoming mail goes
dig +short A mx1.example.com                  # does each MX target resolve?
dig +short TXT example.com | grep -i spf1     # SPF
dig +short TXT _dmarc.example.com             # DMARC
dig +short TXT s1._domainkey.example.com      # DKIM, if you know the selector (s1 here)

On Windows:

nslookup -type=mx example.com
nslookup -type=txt _dmarc.example.com
Resolve-DnsName example.com -Type MX
Resolve-DnsName _dmarc.example.com -Type TXT

A healthy self-hosted domain looks like this:

$ dig +short MX example.com
10 mx1.example.com.
20 mx2.example.com.

$ dig +short A mx1.example.com
203.0.113.25

$ dig +short TXT example.com | grep -i spf1
"v=spf1 mx include:_spf.google.com ~all"

$ dig +short TXT _dmarc.example.com
"v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@example.com"

If you’d rather not run commands, the email check does all of this in one go and adds blacklist and policy checks; what it does and doesn’t cover is at the end of this page. The DNS lookup shows any single record exactly as published.

Below is what to look for in each answer.

MX: where incoming mail goes

Each MX line is a priority and a hostname. Senders try the lowest number first; equal numbers share the load. Typical provider values:

  • Google Workspace: 1 smtp.google.com. for newer setups, or the older set of five aspmx.l.google.com-style records. Both work.
  • Microsoft 365: one record like 0 example-com.mail.protection.outlook.com. (some newer setups get a name under mx.microsoft instead).

What goes wrong:

  • No MX at all. Senders then fall back to the domain’s A record and try to deliver to your web server, which usually doesn’t accept mail. If the domain really receives no mail, say so with a null MX: example.com. IN MX 0 . (RFC 7505).
  • An IP address instead of a hostname. 10 203.0.113.25 is invalid. MX must name a host, and that host has the A/AAAA record.
  • The target doesn’t resolve. A typo, or a host that was deleted. Run the second command for every MX target.
  • The target is a CNAME. The standards don’t allow it. Many senders cope; some don’t. Point MX at a name with its own A record.
  • Leftovers from a migration. The new provider’s record at priority 1 and the old one still at 10 means mail goes to the old system whenever the new one doesn’t answer. Delete old MX records once you’ve moved.
  • The mail hostname is behind a web proxy. On Cloudflare, a proxied (orange-cloud) record only carries web traffic; port 25 to that name goes nowhere. Mail hostnames must be DNS-only.

DNS being right doesn’t prove the server answers. From a machine that’s allowed to make outbound connections on port 25 (many home ISPs and cloud providers block it, so a failure from your laptop proves little):

$ nc -v mx1.example.com 25
Connection to mx1.example.com 25 port [tcp/smtp] succeeded!
220 mx1.example.com ESMTP Postfix

A 220 greeting means a mail server is listening. To see the certificate it offers for STARTTLS:

openssl s_client -starttls smtp -connect mx1.example.com:25 -servername mx1.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -dates

SPF: who may send

SPF is a TXT record at the domain itself, starting with v=spf1. Domains carry other TXT records (site verifications and so on), so filter for spf1.

What goes wrong:

  • Two SPF records. Two TXT records starting with v=spf1 is a permanent error; receivers treat the domain as having no valid SPF. This often happens when a second provider’s setup guide says “add this record” and someone does, literally. Merge them into one.
  • Too many lookups. include, a, mx, ptr, exists and redirect each cost a DNS lookup, includes inside includes count too, and the limit is ten. ip4: and ip6: cost nothing. SPF PermError: too many DNS lookups shows how to count the whole tree and cut it down.
  • +all at the end authorizes the entire internet. Use ~all or -all.
  • Includes for services you stopped using. They waste lookups and keep authorizing someone else to send as you.
  • SPF on the wrong name. SPF isn’t inherited. If news.example.com sends mail with that domain in the envelope, it needs its own record at news.example.com.

SPF checks the envelope sender (Return-Path), not the From: address, so an SPF record can be perfect and still not help DMARC if your mail service uses its own bounce domain. The email deliverability guide covers that.

DMARC: the policy

DMARC is a TXT record at _dmarc. plus the domain. Look for:

  • It’s at the right name. _dmarc.example.com, not the domain itself, not dmarc.example.com.
  • v=DMARC1 comes first, followed by p=.
  • Exactly one record. Two DMARC records mean receivers apply none.
  • The policy. p=none is monitoring only and is what security scanners flag. quarantine and reject are enforcement. Moving up safely is covered in DMARC p=none vs quarantine vs reject.
  • rua= is set, so someone receives the aggregate reports. Without it you’re flying blind.

If a subdomain has no DMARC record of its own, receivers use the organizational domain’s record, with its sp= policy if one is set. So a missing _dmarc.mail.example.com isn’t necessarily a gap.

DKIM: you need the selector

DKIM keys live at selector._domainkey.domain, and DNS gives you no way to list them. To find the right name, open a message the domain sent, find the DKIM-Signature header, and read d= (the domain) and s= (the selector).

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=google; ...
$ dig +short TXT google._domainkey.example.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3v..." "...IDAQAB"

Several quoted strings in one answer are normal for a 2048-bit key. Two separate answers at that name are not, and neither is an empty p=, which means the key was revoked. With Microsoft 365 and many email services, the selector name is a CNAME to a key they host; dig prints the CNAME line before the key.

Some well-known selectors: google (Google Workspace), selector1 and selector2 (Microsoft 365), protonmail, protonmail2 and protonmail3 (Proton Mail), fm1 to fm3 (Fastmail), s1 and s2 (SendGrid, among others). Email services often pick their own names, so the message header is the only reliable source.

If the record exists but receivers still report failures, that’s a different problem: DKIM fail and “body hash did not verify”.

Checking from outside, and after a change

Your own machine may be using a cached answer. After any change, compare public resolvers with the authoritative server:

dig +short NS example.com
dig +short TXT _dmarc.example.com @ns1.your-dns-host.net   # the authoritative answer
dig +short TXT _dmarc.example.com @1.1.1.1
dig +short TXT _dmarc.example.com @8.8.8.8

If the authoritative server has the new value and the public resolvers still show the old one, it’s caching. They’ll catch up when the old record’s TTL runs out.

What the DechoNet email check covers

The email check runs these lookups through public DNS-over-HTTPS and scores the result. To read it correctly:

  • MX, SPF, DMARC and DKIM each carry a quarter of the score. A DMARC policy of none and an SPF record over the lookup limit each cost a smaller deduction.
  • The SPF lookup count covers the top-level record only. It counts the lookup mechanisms written in your record; it doesn’t follow your includes, so the real total can be higher.
  • DKIM is found by guessing. It tries 18 common selectors: google, default, k1, selector1, selector2, s1, s2, protonmail, protonmail2, protonmail3, mesmtp, fm1, fm2, fm3, zoho, 20230601, 20221208 and 20210112. “Not detected” means none of those names has a key, not that the domain doesn’t sign.
  • Blacklists are checked for the MX hosts: the IPs of up to 4 MX servers against Spamhaus ZEN, SpamCop, Barracuda and PSBL. That’s your inbound servers. If you send through a different service, enter your sending IP instead; in IP mode the tool checks that IP’s blacklist status and its reverse DNS (PTR). Spamhaus refuses queries that come through public resolvers, so it always shows as “Could not check” rather than clean.
  • BIMI, MTA-STS, TLS-RPT and DANE are shown for information and don’t affect the score.

For setting up what’s missing, in order, start with the email deliverability guide.

Check your own domain now

Free, no sign-up. Runs the exact check this guide describes and shows what to fix.