Introduction

DMARC (Domain-based Message Authentication, Reporting, and Conformance) ties together SPF and DKIM to tell receiving mail servers how to handle email that fails authentication. When the DMARC DNS record is wrong, missing, or malformed, email deliverability suffers silently. Mail goes to spam, gets rejected without explanation, or worse - domain spoofing goes undetected because reporting is broken.

Symptoms

Email deliverability drops with no clear cause, or DMARC reports show authentication failures:

``` # DMARC lookup fails dig _dmarc.example.com TXT +short # (empty or missing)

# Mail servers report DMARC failure Authentication-Results: mx.google.com; dmarc=fail (p=none dis=none) header.from=example.com

# Senders receive bounce messages 550 5.7.1 Email rejected due to DMARC policy

# DMARC aggregate reports show failures <record> <row> <source_ip>192.0.2.1</source_ip> <count>42</count> <policy_evaluated> <disposition>none</disposition> <dkim>fail</dkim> <spf>fail</spf> </policy_evaluated> </row> </record> ```

Online DMARC checkers report errors:

bash
Error: No DMARC record found for domain example.com
Warning: DMARC policy is "none" - no enforcement
Error: DMARC record syntax error: invalid tag value

Common Causes

  1. 1.DMARC record missing - Never created or accidentally deleted
  2. 2.Wrong hostname - Record at _dmarc.example.com instead of _dmarc.example.com
  3. 3.Syntax errors - Invalid tags, missing semicolons, malformed URI
  4. 4.Policy too aggressive - p=reject before testing with p=none
  5. 5.RUA/RUF email unreachable - Reporting addresses do not exist or cannot receive
  6. 6.SPF/DKIM alignment mismatch - Records exist but do not align with From domain

Step-by-Step Fix

Step 1: Verify Current DMARC Record

Query for the DMARC TXT record:

```bash # Query DMARC record dig _dmarc.example.com TXT +short

# Check authoritative server dig @ns1.example.com _dmarc.example.com TXT

# Using nslookup nslookup -type=TXT _dmarc.example.com

# Check for multiple DMARC records (error - only one allowed) dig _dmarc.example.com TXT +short | wc -l ```

Expected output should look like:

bash
"v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com; pct=100; adkim=s; aspf=s"

Step 2: Validate DMARC Record Syntax

Check each component of the DMARC record:

bash
# Parse and validate using online tools or:
# v=DMARC1 - Version (required, must be first)
# p= - Policy: none, quarantine, reject (required)
# rua= - Aggregate report URI (optional)
# ruf= - Forensic report URI (optional)
# pct= - Percentage of messages to apply policy (optional, default 100)
# adkim= - DKIM alignment: s (strict) or r (relaxed)
# aspf= - SPF alignment: s (strict) or r (relaxed)
# fo= - Failure reporting options: 0, 1, d, s (optional)
# ri= - Report interval in seconds (optional)

Common syntax errors:

``` # WRONG: Missing semicolon "v=DMARC1 p=none"

# WRONG: Wrong version capitalization "v=dmarc1; p=none"

# WRONG: Invalid policy value "v=DMARC1; p=block"

# WRONG: Invalid URI scheme "v=DMARC1; p=none; rua=http://reports.example.com/dmarc"

# CORRECT: "v=DMARC1; p=none; rua=mailto:dmarc@example.com" ```

Step 3: Check SPF and DKIM Prerequisites

DMARC requires both SPF and/or DKIM to pass. Verify they are configured:

```bash # Check SPF record dig example.com TXT +short | grep v=spf1

# Check DKIM record (selector varies by mail server) dig default._domainkey.example.com TXT +short dig selector1._domainkey.example.com TXT +short

# For common mail providers: # Google Workspace dig google._domainkey.example.com TXT +short

# Microsoft 365 dig selector1-example-com._domainkey.example.onmicrosoft.com TXT +short ```

Step 4: Create or Fix DMARC Record

For BIND zone files:

```bash # Edit zone file sudo vi /etc/bind/db.example.com

# Add DMARC record (start with p=none for monitoring) _dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com; pct=100; adkim=r; aspf=r"

# Increment SOA serial named-checkzone example.com /etc/bind/db.example.com sudo rndc reload example.com ```

For Cloudflare:

bash
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  --data '{
    "type": "TXT",
    "name": "_dmarc",
    "content": "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com; pct=100; adkim=r; aspf=r",
    "ttl": 3600
  }'

For Active Directory DNS:

powershell
Add-DnsServerResourceRecord -Txt -Name "_dmarc" -ZoneName "example.com" `
    -DescriptiveText "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com; pct=100; adkim=r; aspf=r" `
    -TimeToLive 3600

Step 5: Configure Reporting Email Addresses

The rua and ruf addresses must exist and be able to receive reports:

```bash # Create the dmarc-reports mailbox # Postfix example: sudo useradd -m -s /bin/bash dmarc-reports sudo passwd dmarc-reports

# Or alias to existing address echo "dmarc-reports: postmaster@example.com" | sudo tee -a /etc/aliases sudo newaliases

# Verify email delivery echo "Test" | mail -s "DMARC Test" dmarc-reports@example.com ```

For external reporting services (recommended):

``` # Use a DMARC analysis provider "v=DMARC1; p=none; rua=mailto:reports@dmarcian.com"

# Multiple report destinations "v=DMARC1; p=none; rua=mailto:dmarc@example.com,mailto:reports@thirdparty.com" ```

Step 6: Implement DMARC Gradually

Phase 1: Monitor (p=none)

bash
# Start with monitoring only
_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com; adkim=r; aspf=r"

Phase 2: Quarantine (p=quarantine)

```bash # After reviewing reports for 1-2 weeks _dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@example.com; pct=25"

# Gradually increase percentage _dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@example.com; pct=50" _dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@example.com; pct=100" ```

Phase 3: Reject (p=reject)

bash
# Only after quarantining works well
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc-reports@example.com; pct=100"

Step 7: Verify and Test

Test DMARC record from external resolvers:

bash
dig @8.8.8.8 _dmarc.example.com TXT +short
dig @1.1.1.1 _dmarc.example.com TXT +short

Send test email and check headers:

```bash # Send test email echo "DMARC test" | mail -s "DMARC Test" recipient@gmail.com

# Check authentication results in received email headers Authentication-Results: mx.google.com; dmarc=pass (p=none dis=none) header.from=example.com spf=pass (google.com: domain of user@example.com designates 192.0.2.1 as permitted sender) smtp.mailfrom=user@example.com dkim=pass header.i=@example.com ```

Use online DMARC validators:

bash
# Check with MXToolbox, DMARC Analyzer, or similar
# https://mxtoolbox.com/dmarc.aspx
# https://dmarcian.com/domain-checker/

Common Pitfalls

  • Going straight to p=reject - Causes legitimate email to bounce during testing phase
  • Multiple DMARC records - Creates ambiguous policy, only one allowed
  • Unreachable rua address - Reports bounce, missing visibility into problems
  • Strict alignment without DKIM - aspf=s only works if SPF aligns perfectly
  • Forgetting subdomains - Need separate DMARC records or inheritance
  • TXT record too long - Some DNS servers have issues with long TXT records, may need splitting

Best Practices

  • Always start with p=none and monitor for at least 2 weeks before enforcement
  • Use a third-party DMARC analysis service for report aggregation
  • Test from multiple mail providers (Gmail, Outlook, Yahoo) before enforcement
  • Keep rua email address monitored and responsive
  • Document the DMARC policy in your organization
  • Review DMARC reports weekly during rollout phase
  • DNS DKIM Record Error
  • DNS SPF Record Configuration
  • Email Deliverability Issues
  • DNS TXT Record Syntax