Email Rejected Due to SPF/DKIM/DMARC Failures
症状
- Emails arriving in spam/junk folder despite correct content
- SMTP rejection: `554 5.7.5 Permanent error evaluating DMARC policy`
- Received email headers show `Authentication-Results: dkim=fail (signature verification failed)`
- DMARC aggregate reports (RUA) show high `fail` count for your domain
- Gmail Postmaster Tools flags your domain alignment as failing
- SMTP rejection: `554 5.7.5 Permanent error evaluating DMARC policy`
- Received email headers show `Authentication-Results: dkim=fail (signature verification failed)`
- DMARC aggregate reports (RUA) show high `fail` count for your domain
- Gmail Postmaster Tools flags your domain alignment as failing
根本原因
- SPF record missing, too broad (use of +all), or exceeds 10 DNS lookup limit
- DKIM private key on mail server does not match the public key in DNS TXT record
- DMARC policy set to p=reject or p=quarantine before SPF/DKIM are confirmed passing
- Sending from a subdomain without a subdomain-specific SPF/DKIM/DMARC record
- Third-party sender (ESP, CRM) not added to SPF include list or missing DKIM CNAME
診断
**Step 1 — Verify SPF record**
```bash
dig TXT yourdomain.com +short | grep 'v=spf1'
# Example good record:
# "v=spf1 include:_spf.google.com include:sendgrid.net ~all"
# Count DNS lookups (must be ≤ 10)
# Each 'include:', 'a:', 'mx:' counts as one lookup
```
**Step 2 — Verify DKIM record**
```bash
# Selector is usually 'default', 'google', 'k1', or check email headers
SELECTOR=default
dig TXT ${SELECTOR}._domainkey.yourdomain.com +short
# Expected: "v=DKIM1; k=rsa; p=MIIBIjAN..."
# Empty response means the DNS record is missing
```
**Step 3 — Inspect email headers for auth results**
```
# In Gmail: three-dot menu → Show original
# Look for:
Authentication-Results: mx.google.com;
dkim=pass [email protected];
spf=pass (google.com: domain of ...) smtp.mailfrom=yourdomain.com;
dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=yourdomain.com
```
**Step 4 — Check DMARC record and alignment**
```bash
dig TXT _dmarc.yourdomain.com +short
# Example: "v=DMARC1; p=quarantine; rua=mailto:[email protected]; pct=100"
# Start with p=none to monitor before enforcing p=quarantine or p=reject
```
**Step 5 — Use online validation tools**
```bash
# Send a test email to:
# [email protected] (returns full auth report)
# Or use: https://www.mail-tester.com
```
```bash
dig TXT yourdomain.com +short | grep 'v=spf1'
# Example good record:
# "v=spf1 include:_spf.google.com include:sendgrid.net ~all"
# Count DNS lookups (must be ≤ 10)
# Each 'include:', 'a:', 'mx:' counts as one lookup
```
**Step 2 — Verify DKIM record**
```bash
# Selector is usually 'default', 'google', 'k1', or check email headers
SELECTOR=default
dig TXT ${SELECTOR}._domainkey.yourdomain.com +short
# Expected: "v=DKIM1; k=rsa; p=MIIBIjAN..."
# Empty response means the DNS record is missing
```
**Step 3 — Inspect email headers for auth results**
```
# In Gmail: three-dot menu → Show original
# Look for:
Authentication-Results: mx.google.com;
dkim=pass [email protected];
spf=pass (google.com: domain of ...) smtp.mailfrom=yourdomain.com;
dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=yourdomain.com
```
**Step 4 — Check DMARC record and alignment**
```bash
dig TXT _dmarc.yourdomain.com +short
# Example: "v=DMARC1; p=quarantine; rua=mailto:[email protected]; pct=100"
# Start with p=none to monitor before enforcing p=quarantine or p=reject
```
**Step 5 — Use online validation tools**
```bash
# Send a test email to:
# [email protected] (returns full auth report)
# Or use: https://www.mail-tester.com
```
解決
**Fix 1 — Add or correct SPF record**
```dns
; Replace +all with ~all (softfail) or -all (hard fail)
yourdomain.com. IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net -all"
```
**Fix 2 — Re-publish DKIM key**
```bash
# Generate new 2048-bit RSA key pair (Postfix + OpenDKIM)
opendkim-genkey -b 2048 -d yourdomain.com -s default
# Publishes default.private and default.txt
cat default.txt # Copy p= value to DNS TXT record
# Restart OpenDKIM
sudo systemctl restart opendkim postfix
```
**Fix 3 — Start DMARC in monitoring mode**
```dns
_dmarc.yourdomain.com. IN TXT "v=DMARC1; p=none; rua=mailto:[email protected]"
; After 2 weeks with clean reports, escalate to p=quarantine, then p=reject
```
**Fix 4 — Add third-party senders to SPF**
```dns
; Add ESP include entries
yourdomain.com. IN TXT "v=spf1 include:_spf.google.com include:amazonses.com -all"
; For DKIM, add CNAME records per ESP instructions:
s1._domainkey.yourdomain.com. IN CNAME s1.domainkey.u12345.wl123.sendgrid.net.
```
```dns
; Replace +all with ~all (softfail) or -all (hard fail)
yourdomain.com. IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net -all"
```
**Fix 2 — Re-publish DKIM key**
```bash
# Generate new 2048-bit RSA key pair (Postfix + OpenDKIM)
opendkim-genkey -b 2048 -d yourdomain.com -s default
# Publishes default.private and default.txt
cat default.txt # Copy p= value to DNS TXT record
# Restart OpenDKIM
sudo systemctl restart opendkim postfix
```
**Fix 3 — Start DMARC in monitoring mode**
```dns
_dmarc.yourdomain.com. IN TXT "v=DMARC1; p=none; rua=mailto:[email protected]"
; After 2 weeks with clean reports, escalate to p=quarantine, then p=reject
```
**Fix 4 — Add third-party senders to SPF**
```dns
; Add ESP include entries
yourdomain.com. IN TXT "v=spf1 include:_spf.google.com include:amazonses.com -all"
; For DKIM, add CNAME records per ESP instructions:
s1._domainkey.yourdomain.com. IN CNAME s1.domainkey.u12345.wl123.sendgrid.net.
```
予防
- Always set SPF, DKIM, and DMARC before sending any production email
- Use p=none for the first 30 days, monitor RUA reports, then enforce p=reject
- Keep the SPF DNS lookup count under 10; use SPF flattening tools if needed
- Rotate DKIM keys annually and after any server compromise
- Audit third-party senders quarterly — remove ESPs no longer in use from SPF/DKIM
- Use p=none for the first 30 days, monitor RUA reports, then enforce p=reject
- Keep the SPF DNS lookup count under 10; use SPF flattening tools if needed
- Rotate DKIM keys annually and after any server compromise
- Audit third-party senders quarterly — remove ESPs no longer in use from SPF/DKIM