Security 6 min read

Postfix Rejecting Mail? Fix SPF, DKIM and DMARC Failures

Mail bouncing back with an SPF, DKIM or DMARC complaint means a specific record or signature is broken, not that Postfix itself is misconfigured.

By BashPilot Team
BashEmail Email spam protection Keep outbound mail clean and off blacklists, automatically. Explore BashEmail

Postfix rejecting mail with an SPF, DKIM or DMARC error in the bounce is almost never a Postfix bug. It means a receiving server checked one of those three things and did not like what it found. The fix depends entirely on which of the three actually failed, so the first job is finding out which one it is.

Read the actual rejection first

Do not start editing DNS records yet. /var/log/mail.log (or journalctl -u postfix on newer Ubuntu) has the exact reason a message was deferred or bounced.

find the rejection reason
grep -i 'reject\|bounce\|dkim\|spf' /var/log/mail.log | tail -40

If you have a bounce in an inbox somewhere, the Authentication-Results header in the original outbound copy (check your Sent items, or postqueue -p if it is still queued) tells you directly: spf=fail, dkim=fail or dmarc=fail. Fix the one that actually failed, checking all three blind wastes time.

SPF: the sending IP does not match the record

SPF is the simplest of the three: it is a DNS TXT record listing which IPs are allowed to send mail for your domain. If your server's IP is not in it, receiving servers see spf=fail.

check your current SPF record
dig txt yourdomain.com +short

A working record looks like v=spf1 ip4:203.0.113.10 include:_spf.google.com ~all. Add your server's outbound IP explicitly with ip4:, do not rely on a or mx mechanisms unless you are certain the sending server is the same as the one those records point to. A soft fail (~all) tells receivers to flag suspicious mail rather than reject outright, a hard fail (-all) tells them to reject it, use -all only once you are confident every legitimate sending source is listed.

DKIM: the signature is missing or invalid

DKIM signs each outgoing message with a private key, and the receiver checks it against a public key published in DNS. On Ubuntu this is normally OpenDKIM sitting in front of Postfix as a milter.

confirm opendkim is running and postfix is calling it
systemctl status opendkim
postconf smtpd_milters non_smtpd_milters

Both smtpd_milters and non_smtpd_milters need to point at OpenDKIM's socket, typically inet:localhost:8891. If either is empty, Postfix is sending mail unsigned regardless of how correctly OpenDKIM itself is configured.

test the key pair actually matches DNS
opendkim-testkey -d yourdomain.com -s default -vvv

A response of key not secure is fine for most setups (it means DNSSEC is not in use, not that the key is broken). A response of key not found or key data mismatch means the TXT record published at default._domainkey.yourdomain.com does not match the private key OpenDKIM is signing with, usually because the key was regenerated after the DNS record was published, or the selector name in /etc/opendkim/keytable does not match the one in DNS.

DMARC: SPF or DKIM passed but do not align

DMARC failing while SPF and DKIM both technically pass usually means an alignment mismatch: the domain in SPF's Return-Path or DKIM's d= tag does not match the visible From address domain. This commonly happens when mail is relayed through a third-party service (a CRM, a support desk tool) that signs with its own domain instead of yours.

check the current DMARC record
dig txt _dmarc.yourdomain.com +short

Start with v=DMARC1; p=none; rua=mailto:dmarc-reports@yourdomain.com; while you sort out alignment, since p=none requests reports without rejecting anything. Read a few days of aggregate reports to see exactly which sending source is failing alignment before moving to p=quarantine or p=reject. Tightening the policy before every legitimate sender is aligned will bounce your own mail.

Chasing down one broken record across SPF, DKIM and DMARC on a single domain is routine. Doing it for every domain hosted on a server, every time a new site gets added, is the part that turns into a full afternoon. BashEmail checks all three records and OpenDKIM's actual signing status for every hosted domain, and flags exactly which one is broken instead of leaving you to run three separate lookups.

In BashPilot

Diagnosing a DMARC failure across every domain on the box

app.bashpilot.com
mail to gmail is bouncing on this server, dmarc fail
BashPilot

Checked SPF, DKIM and DMARC for all 6 hosted domains. One domain is misaligned.

DomainSPFDKIMDMARC alignment
shop.example.compasspassfail, d= does not match From
5 otherspasspassaligned
what is causing the misalignment on shop.example.com
BashPilot

Its OpenDKIM signing table entry uses a leftover selector from a previous mail provider migration, so it is signing with the old d= value. Updating the signingtable entry and reloading opendkim will fix it.

bash
sed -i 's/old-selector/default/' /etc/opendkim/signingtable
systemctl reload opendkim
Example output, not a screenshot of a specific customer's server.

Try this on one of your own servers.

Start free trial

Verify the fix actually landed

Do not trust dig output alone, DNS caching can show you a stale record for hours. Send a live test message and read the real result.

  • Send a message to check-auth@verifier.port25.com or use mail-tester.com, both return a full SPF/DKIM/DMARC breakdown within a minute.
  • If you have a Gmail account handy, send to it and open the message, then View Original to read the live Authentication-Results header directly.
  • Re-run opendkim-testkey after any DNS change and wait for your resolver's TTL to expire first, a cached negative result will still show as failing.

Prevent this from recurring

  • Set the server's PTR (reverse DNS) record with your hosting provider to match the hostname Postfix announces in its HELO or EHLO. A missing PTR is treated as suspicious by most receiving servers independent of SPF, DKIM or DMARC.
  • Keep SPF, DKIM and DMARC on a checklist for every new domain you add to the server, not just the first one you configured.
  • Subscribe to DMARC aggregate reports (rua=) permanently, not just while debugging, they catch a broken signature before it turns into a bounce.
Share LinkedIn X
Questions

Frequently asked questions

How do I check if my server's IP is on a DNSBL blacklist?

Run `dig -x YOUR_IP` to confirm your PTR record resolves correctly, then check the IP against a lookup tool such as mxtoolbox.com/blacklists. A missing or mismatched PTR record is one of the most common reasons a brand new server's mail gets rejected before SPF or DKIM are even checked.

Why does Gmail show SPF fail even though my SPF record looks correct?

Check whether the message was relayed through a forwarding service or mailing list, which changes the sending IP without updating the SPF record's origin. Also confirm you are not exceeding SPF's 10 DNS lookup limit across nested `include:` mechanisms, which causes a permanent SPF error regardless of the record's content.

How do I test if OpenDKIM is actually signing outgoing messages?

Send a test email to your own address on a different domain and inspect the raw headers for a `DKIM-Signature` line. If it is missing entirely, check that `smtpd_milters` and `non_smtpd_milters` in Postfix's config point to OpenDKIM's socket and that the sending domain has an entry in `/etc/opendkim/signingtable`.

What is the difference between SPF alignment and DKIM alignment in DMARC?

SPF alignment compares the domain in the SMTP envelope's Return-Path against the visible From address domain. DKIM alignment compares the `d=` domain in the DKIM signature against the same From address. DMARC passes if either one aligns, so a message can pass DMARC even if only one of SPF or DKIM is aligned.

Do I still need DMARC if SPF and DKIM are both already configured?

Yes. SPF and DKIM only tell a receiver whether a check passed, DMARC is what tells the receiver what to do about a failure, and gives you visibility through aggregate reports into spoofing attempts you would otherwise never see.

Put your servers on autopilot.

Connect a server in about a minute. The first week is on us.