How to check mx records

Run nslookup -type=MX mozilla.org 8.8.8.8 and read the preference number on each line. Lower wins, so preference 1 is tried first and preference 10 last. Equal numbers leave the choice to the sending server. Then confirm every exchanger resolves to an address and is not itself an alias.

Checker offline. Follow the manual steps below, they give the same answer.

Why check this

Run this before a mail provider swap goes live, and again on the staging sign-off of any release that sends mail to internal addresses. The failure it prevents is the one where the application sends fine, the SMTP log says accepted, and the mail lands at the previous provider because one of four MX records was never moved.

The check answers two questions. Which hosts accept mail for this domain, and in what order does a sender try them. It does not tell you that those hosts answer on port 25, and it does not tell you that mail reaching them is delivered to a mailbox. It is a routing read, not a delivery test.

Prerequisites

Steps

  1. Step 1.

    Read the MX set of a domain that publishes several.

    nslookup -type=MX mozilla.org 8.8.8.8
    
    Non-authoritative answer:
    Server:  dns.google
    Address:  8.8.8.8
    
    mozilla.org	MX preference = 1, mail exchanger = aspmx.l.google.com
    mozilla.org	MX preference = 10, mail exchanger = aspmx3.googlemail.com
    mozilla.org	MX preference = 5, mail exchanger = alt2.aspmx.l.google.com
    mozilla.org	MX preference = 5, mail exchanger = alt1.aspmx.l.google.com

    Four records, printed in the order 1, 10, 5, 5. The print order carries no meaning. A sender tries aspmx.l.google.com first because 1 is the lowest number, then either of the two records at 5, then aspmx3.googlemail.com.

  2. Step 2.

    Sort by preference and validate each target in one pass. Save this as mx.js and run node mx.js mozilla.org.

    const { Resolver } = require('node:dns');
    const r = new Resolver();
    r.setServers(['8.8.8.8']);
    const name = process.argv[2];
    r.resolveMx(name, (err, recs) => {
      if (err) { console.log('MX ' + err.code); return; }
      recs.sort((a, b) => a.priority - b.priority);
      let i = 0;
      const next = () => {
        if (i >= recs.length) return;
        const m = recs[i++];
        r.resolveCname(m.exchange, (ce, cn) => {
          r.resolve4(m.exchange, (ae, addrs) => {
            console.log(
              String(m.priority).padStart(3) + '  ' + m.exchange.padEnd(38) +
              ' cname=' + (ce ? ce.code : cn.join(',')) +
              ' a=' + (ae ? ae.code : addrs.length + ' addr')
            );
            next();
          });
        });
      };
      next();
    });
    
      1  aspmx.l.google.com                     cname=ENODATA a=1 addr
    5  alt2.aspmx.l.google.com                cname=ENODATA a=1 addr
    5  alt1.aspmx.l.google.com                cname=ENODATA a=1 addr
    10  aspmx3.googlemail.com                  cname=ENODATA a=1 addr

    A healthy target has no CNAME and at least one address. Both columns say so here.

  3. Step 3.

    Learn what the two defective shapes look like in the same columns. Save this as mxt.js and run node mxt.js www.github.com _dmarc.mozilla.org aspmx.l.google.com.

    const { Resolver } = require('node:dns');
    const r = new Resolver();
    r.setServers(['8.8.8.8']);
    let i = 2;
    const next = () => {
      if (i >= process.argv.length) return;
      const n = process.argv[i++];
      r.resolveCname(n, (ce, cn) => {
        r.resolve4(n, (ae, addrs) => {
          console.log(n.padEnd(30) + ' cname=' + (ce ? ce.code : cn.join(',')) + ' a=' + (ae ? ae.code : addrs.join(',')));
          next();
        });
      });
    };
    next();
    
    www.github.com                 cname=github.com a=140.82.121.4
    _dmarc.mozilla.org             cname=ENOTFOUND a=ENODATA
    aspmx.l.google.com             cname=ENODATA a=142.251.127.27

    The first line is an alias. An MX record whose target looks like that is invalid, because RFC 5321 requires the target to be a name with an address record, not a name that redirects. The second line is a name with no address at all. An MX pointing there gives a sender nothing to connect to. The third line is what a correct target looks like.

  4. Step 4.

    Read a domain that declares it accepts no mail, and see what each tool calls it.

    nslookup -type=MX example.com 8.8.8.8
    
    Non-authoritative answer:
    Server:  dns.google
    Address:  8.8.8.8
    
    example.com	MX preference = 0, mail exchanger = (root)

    (root) is the root of the DNS tree written as a name, which is the null MX of RFC 7505. Node reports the same record as [{"exchange":"","priority":0}], an empty string.

How to read the result

| What you see | What it means | What to do | | --- | --- | --- | | Several records with different preferences | Lowest preference is tried first, the rest are fallbacks | Confirm each fallback accepts mail, not only the first | | Two records with the same preference | The sender picks between them | Both must work. Neither is a spare | | mail exchanger = (root) or "exchange":"" | Null MX. The domain publishes that it receives no mail | Correct for a send-only domain, a defect on one that accepts mail | | cname= shows a name | The MX target is an alias, which RFC 5321 forbids | Replace the target with a name that carries the address itself | | a=ENODATA on a target | The target name exists and has no address | Add the address record or point the MX somewhere else | | MX ENODATA for the domain | No MX record. Senders fall back to the A record | Publish an MX, or a null MX if the domain sends only |

Common mistakes

Sign: A monitoring script reads the MX list, takes the first entry and checks only that host.Cause: The order records arrive in has nothing to do with preference. Step 1 returned 1, 10, 5, 5 from one query. Sort on the preference number before you use position, and remember that two records at the same number are both live paths.
Sign: Node reports an absent CNAME as ENOTFOUND on one name and an absent A as ENODATA on the same name.Cause: Line 2 of step 3 shows both codes for _dmarc.mozilla.org, a name that exists and holds only a TXT record. c-ares does not use one code for the no-record-of-this-type case, so a validator written as err.code === 'ENODATA' treats the CNAME result as a hard failure. Treat ENOTFOUND and ENODATA together when you are asking whether a type is present.
Sign: A check asserts that the MX list is not empty and passes on a domain that accepts no mail.Cause: A null MX is a record. It has length 1 and preference 0, and its target is the empty string. The assertion to write is that the target is a non-empty name, not that the array has entries.

What to check next

FAQ

How to check mx record of a domain?

Query the registrable domain, not a host under it. nslookup -type=MX example.org 8.8.8.8 is the whole command. Mail is addressed to the part after the @, so an MX published at www.example.org routes nothing.

How to check mx record in cmd?

The same line works in cmd, in PowerShell and in a POSIX shell: nslookup -type=MX mozilla.org 8.8.8.8. PowerShell also has Resolve-DnsName mozilla.org -Type MX, which prints the preference in a column named Preference and adds the TTL.

Which MX is used when two have the same preference?

The sending server chooses, and nothing in the protocol says how. Both records are in active use. Test both, and size both for the full volume, because a sender is allowed to send everything to one of them.

Does a lower preference number mean lower priority?

No. The number is a preference, and the record with the smallest number is tried first. Preference 1 outranks preference 10. The field is often labelled priority in control panels, which reverses the everyday sense of the word.

Verified

Verified by Maks Vernynslookup Windows 11 build 22631node 22.23.2

Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.

basic6 minpublished updated Maks Verny