DMARC (Domain-based Message Authentication, Reporting & Conformance)

Understanding DMARC and how it enforces email authentication policies.


What is DMARC?

DMARC (Domain-based Message Authentication, Reporting & Conformance) is an email authentication protocol that builds on SPF and DKIM to:

  1. Enforce authentication policies - Tell receivers what to do with emails that fail SPF/DKIM
  2. Prevent domain spoofing - Stop attackers from using your domain
  3. Provide visibility - Receive reports on who's sending email using your domain
  4. Improve deliverability - Build trust with email providers

DMARC is the final piece that ties SPF and DKIM together into a complete email authentication framework.

The Problem DMARC Solves

SPF and DKIM alone have limitations:

  • SPF only checks the envelope sender (MAIL FROM), not the visible From address
  • DKIM passes even if the signing domain differs from the From address
  • No enforcement - Even with SPF/DKIM, receivers decide what to do with failures

DMARC fixes this by requiring alignment with the From address and providing explicit policy enforcement.


How DMARC Works

sequenceDiagram
    participant Sender as Mail Server
    participant DNS as DNS Server
    participant Receiver as Receiving Server

    Sender->>Receiver: Email from user@example.com

    Receiver->>Receiver: 1. Check SPF
    Note over Receiver: SPF Pass ✓

    Receiver->>Receiver: 2. Check DKIM
    Note over Receiver: DKIM Pass ✓

    Receiver->>DNS: 3. Query DMARC policy<br/>_dmarc.example.com

    DNS->>Receiver: v=DMARC1; p=reject;<br/>rua=mailto:dmarc@example.com

    Receiver->>Receiver: 4. Check Alignment<br/>SPF/DKIM domain vs From domain

    alt SPF or DKIM aligned
        Receiver->>Receiver: DMARC Pass - Deliver
    else Neither aligned + p=reject
        Receiver->>Receiver: DMARC Fail - Reject
    else Neither aligned + p=quarantine
        Receiver->>Receiver: DMARC Fail - Spam folder
    else Neither aligned + p=none
        Receiver->>Receiver: DMARC Fail - Deliver + Report
    end

    Receiver->>Sender: 5. Send aggregate report
    Note over Receiver,Sender: Daily/weekly reports

The DMARC Process

  1. Authentication - Email is checked with SPF and DKIM
  2. Alignment Check - SPF/DKIM domains must align with From header
  3. Policy Lookup - Receiver fetches DMARC policy from DNS
  4. Policy Application - Receiver takes action based on policy (none/quarantine/reject)
  5. Reporting - Receiver sends aggregate and forensic reports

DMARC Record Anatomy

Basic Structure

_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"

All DMARC Tags

Tag Required Description Values Example
v= ✅ Yes Protocol version DMARC1 v=DMARC1
p= ✅ Yes Policy for domain none, quarantine, reject p=reject
sp= ❌ No Subdomain policy none, quarantine, reject sp=quarantine
rua= ❌ No Aggregate report URI mailto: addresses rua=mailto:dmarc@example.com
ruf= ❌ No Forensic report URI mailto: addresses ruf=mailto:forensic@example.com
pct= ❌ No Percentage of messages 0-100 pct=100
adkim= ❌ No DKIM alignment mode r (relaxed), s (strict) adkim=r
aspf= ❌ No SPF alignment mode r (relaxed), s (strict) aspf=r
rf= ❌ No Report format afrf, iodef rf=afrf
ri= ❌ No Report interval (seconds) 0-2147483647 ri=86400
fo= ❌ No Forensic options 0, 1, d, s fo=1

DMARC Policies

The p= tag defines what receiving servers should do with emails that fail DMARC.

Policy Options

p=none (Monitoring Mode)

v=DMARC1; p=none; rua=mailto:dmarc@example.com

Action: Do nothing, just report

When to use: - Starting DMARC - Monitor first before enforcing - Testing changes - Verify configuration works - Gathering data - Understand your email sources

Pros: - ✅ No risk of blocking legitimate email - ✅ Collect data on authentication failures - ✅ Identify unauthorized senders

Cons: - ❌ Provides no protection (emails still delivered) - ❌ Attackers can still spoof your domain

p=quarantine (Spam Folder)

v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com

Action: Mark as spam/suspicious

When to use: - After monitoring with p=none for 1-2 weeks - Gradual enforcement - Less aggressive than reject - High confidence in legitimate sources

Pros: - ✅ Protects users while allowing review - ✅ Failed emails accessible if needed - ✅ Lower risk than reject

Cons: - ❌ Some spoofed emails still reach users - ❌ Less protection than reject

p=reject (Maximum Protection)

v=DMARC1; p=reject; rua=mailto:dmarc@example.com

Action: Reject email completely

When to use: - After successful quarantine period - 100% confidence in configuration - Maximum security required

Pros: - ✅ Best protection against spoofing - ✅ Builds strong sender reputation - ✅ Industry best practice

Cons: - ❌ Risk of blocking legitimate email if misconfigured - ❌ No way to recover rejected emails


DMARC Alignment

Alignment is the key concept in DMARC - it links SPF/DKIM to the visible From address.

What is Alignment?

For DMARC to pass, at least ONE of the following must be true:

  1. SPF passes AND SPF domain aligns with From header
  2. DKIM passes AND DKIM domain aligns with From header

Relaxed Alignment (Default)

Organizational domains must match:

From:        user@example.com
SPF MAIL FROM: bounce@mail.example.com
✓ Aligned (example.com matches)

From:        user@example.com
DKIM d=:     mail.example.com
✓ Aligned (example.com matches)

Configuration:

v=DMARC1; p=reject; adkim=r; aspf=r

Strict Alignment

Exact domains must match:

From:        user@example.com
SPF MAIL FROM: bounce@mail.example.com
✗ NOT aligned (mail.example.com ≠ example.com)

From:        user@example.com
DKIM d=:     example.com
✓ Aligned (exact match)

Configuration:

v=DMARC1; p=reject; adkim=s; aspf=s

Alignment Examples

Example 1: SPF Aligned, DKIM Not Aligned

From:           user@example.com
MAIL FROM:      bounce@example.com (SPF Pass + Aligned ✓)
DKIM d=:        mailserver.com (DKIM Pass but NOT Aligned ✗)

Result: DMARC PASS (SPF alignment sufficient)

Example 2: DKIM Aligned, SPF Not Aligned

From:           user@example.com
MAIL FROM:      list@mailinglist.com (SPF Pass but NOT Aligned ✗)
DKIM d=:        example.com (DKIM Pass + Aligned ✓)

Result: DMARC PASS (DKIM alignment sufficient)

Example 3: Neither Aligned

From:           user@example.com
MAIL FROM:      attacker@evil.com (SPF Pass but NOT Aligned ✗)
DKIM d=:        evil.com (DKIM Pass but NOT Aligned ✗)

Result: DMARC FAIL (no alignment)
Action: Apply policy (none/quarantine/reject)

DMARC Reports

DMARC provides two types of reports for visibility into email authentication.

Aggregate Reports (RUA)

Daily/weekly XML reports summarizing authentication results.

rua=mailto:dmarc@example.com

What's included: - Source IP addresses sending email - SPF/DKIM/DMARC pass/fail counts - Message volume by sender - Policy applied

Example aggregate report structure:

<feedback>
  <report_metadata>
    <org_name>google.com</org_name>
    <date_range>
      <begin>1634169600</begin>
      <end>1634256000</end>
    </date_range>
  </report_metadata>
  <record>
    <row>
      <source_ip>192.0.2.1</source_ip>
      <count>125</count>
    </row>
    <identifiers>
      <header_from>example.com</header_from>
    </identifiers>
    <auth_results>
      <spf>
        <result>pass</result>
      </spf>
      <dkim>
        <result>pass</result>
      </dkim>
    </auth_results>
  </record>
</feedback>

Forensic Reports (RUF)

Real-time failure reports with email samples (rarely supported).

ruf=mailto:forensic@example.com

What's included: - Full headers of failed message - Authentication failure details - Copy of message (redacted)

Privacy concerns: - May contain sensitive data - Few providers send forensic reports - Most organizations only use aggregate reports

Multiple Report Addresses

rua=mailto:dmarc@example.com,mailto:reports@vendor.com

Comma-separated for multiple recipients.

External Report Destinations

To send reports to a different domain, add verification:

# Your domain
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:reports@vendor.com"

# Vendor must publish:
example.com._report._dmarc.vendor.com. IN TXT "v=DMARC1"

DMARC Percentage (pct=)

Gradually roll out policy by applying it to only a percentage of messages.

v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc@example.com
  • pct=25 - Apply policy to 25% of failing messages
  • Remaining 75% treated as p=none (deliver + report)

Use Cases

Testing policy safely:

Week 1: p=quarantine; pct=10
Week 2: p=quarantine; pct=25
Week 3: p=quarantine; pct=50
Week 4: p=quarantine; pct=100

Gradual reject rollout:

Week 1: p=reject; pct=10
Week 2: p=reject; pct=25
Week 3: p=reject; pct=100

Best practice: Once confident, set pct=100 for full protection.


Subdomain Policy (sp=)

Control policy for subdomains separately from the main domain.

v=DMARC1; p=reject; sp=quarantine; rua=mailto:dmarc@example.com
  • p=reject - Policy for example.com
  • sp=quarantine - Policy for *.example.com

When to Use Different Subdomain Policy

Example scenario:

# Strict policy for main domain
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; sp=none"

  • Main domain (example.com) uses p=reject
  • Subdomains (*.example.com) use sp=none (monitoring)

Use cases: - Testing subdomains before enforcing - Different security requirements - Third-party services using subdomains

Subdomain-Specific DMARC

Subdomains can have their own DMARC records:

# Main domain
_dmarc.example.com. IN TXT "v=DMARC1; p=reject"

# Subdomain override
_dmarc.mail.example.com. IN TXT "v=DMARC1; p=none"

Subdomain record takes precedence.


Forensic Report Options (fo=)

Control when forensic reports are generated.

Value Description
fo=0 Generate if both SPF and DKIM fail (default)
fo=1 Generate if either SPF or DKIM fails
fo=d Generate if DKIM fails (regardless of SPF)
fo=s Generate if SPF fails (regardless of DKIM)

Example:

v=DMARC1; p=reject; ruf=mailto:forensic@example.com; fo=1

Note: Few email providers send forensic reports due to privacy concerns.


Setting Up DMARC

Step 1: Ensure SPF and DKIM are Working

Check with ReputeAPI:

curl "https://api.reputeapi.com/api/v1/check?domain=example.com" \
  -H "X-API-Key: your-api-key"

Verify: - ✅ SPF record exists and passes - ✅ DKIM keys configured and signing - ✅ No critical issues

Step 2: Start with p=none

_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"

Why start with none: - Collect data without risk - Identify all legitimate senders - Catch misconfigurations

Step 3: Set Up Report Monitoring

Create mailbox for reports:

dmarc@example.com

Or use DMARC report analyzer: - Postmark DMARC - DMARC Analyzer - EasyDMARC - Valimail

Step 4: Monitor for 1-2 Weeks

Review aggregate reports: - Identify all sending sources - Verify 100% pass rate for legitimate mail - Investigate failures

Look for: - ✅ All legitimate senders passing - ❌ Unexpected sources (third-party services, marketing tools) - ❌ Authentication failures

Step 5: Move to p=quarantine

_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com; pct=100"

Optional: Gradual rollout:

# Start with 25%
v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc@example.com

Step 6: Monitor Quarantine

Check for issues: - Users reporting missing emails? - Spam folders catching legitimate mail? - Report any new failures?

Duration: 2-4 weeks minimum

Step 7: Move to p=reject

_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com; pct=100"

Optional: Gradual rollout:

# Start with 10%
v=DMARC1; p=reject; pct=10; rua=mailto:dmarc@example.com

Step 8: Ongoing Monitoring

Regular tasks: - Weekly: Review aggregate reports - Monthly: Audit sender sources - Quarterly: Check authentication rates - Always: Investigate new failures


Common DMARC Configurations

Configuration 1: Just Starting (Monitoring)

_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"

Configuration 2: Intermediate (Quarantine)

_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; pct=100; rua=mailto:dmarc@example.com"

Configuration 3: Maximum Protection (Reject)

_dmarc.example.com. IN TXT "v=DMARC1; p=reject; pct=100; rua=mailto:dmarc@example.com"

Configuration 4: Strict Alignment

_dmarc.example.com. IN TXT "v=DMARC1; p=reject; adkim=s; aspf=s; rua=mailto:dmarc@example.com"

Configuration 5: With Forensic Reports

_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com; ruf=mailto:forensic@example.com; fo=1"

Configuration 6: Different Subdomain Policy

_dmarc.example.com. IN TXT "v=DMARC1; p=reject; sp=quarantine; rua=mailto:dmarc@example.com"

Configuration 7: Multiple Report Addresses

_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com,mailto:reports@dmarcanalyzer.com"

Common DMARC Issues

Issue 1: DMARC Missing

Problem: No DMARC record found

Impact: Most critical security issue (-40 points)

Fix:

_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"

Start with p=none and gradually increase enforcement.

Issue 2: DMARC Policy None

Problem: DMARC set to p=none (monitoring only)

Impact: No protection (-15 points)

Fix: After monitoring, upgrade to quarantine:

_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"

Issue 3: DMARC Policy Quarantine

Problem: Not using maximum protection

Impact: Moderate issue (-5 points)

Fix: Upgrade to reject:

_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"

Issue 4: No Aggregate Reports

Problem: Missing rua= tag

Impact: No visibility (-5 points)

Fix:

v=DMARC1; p=reject; rua=mailto:dmarc@example.com

Issue 5: Low Percentage

Problem: pct= less than 100

Impact: Partial protection (-5 points)

Fix:

v=DMARC1; p=reject; pct=100; rua=mailto:dmarc@example.com

Issue 6: DMARC Syntax Error

Problem: Invalid DMARC record

Common mistakes:

# Missing v=DMARC1
"p=reject; rua=mailto:dmarc@example.com"

# Wrong version
"v=DMARC2; p=reject"

# Invalid policy
"v=DMARC1; p=block"

# Wrong format
"v=DMARC1; p=reject rua=mailto:dmarc@example.com"  # Missing semicolon

Fix: Validate syntax:

_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"


Reading DMARC Reports

Aggregate Report Analysis

Key metrics to monitor:

  1. Pass Rate
  2. Goal: 100% of legitimate email passes
  3. Action: Investigate any failures

  4. Source IPs

  5. Identify all authorized senders
  6. Detect unauthorized senders

  7. Volume Trends

  8. Track email volume over time
  9. Detect anomalies

  10. Policy Application

  11. See how receivers handle failures
  12. Verify policy enforcement

Report Interpretation

Good Report (All Pass)

Source IP: 192.0.2.1 (Google Workspace)
Count: 1,250 messages
SPF: Pass (1,250)
DKIM: Pass (1,250)
DMARC: Pass (1,250)

Action: None needed ✅

Warning Report (Some Failures)

Source IP: 198.51.100.1 (Unknown)
Count: 50 messages
SPF: Fail (50)
DKIM: Fail (50)
DMARC: Fail (50)

Actions: 1. Identify the source 2. If legitimate: Add to SPF, configure DKIM 3. If unauthorized: Monitor for abuse

Critical Report (Spoofing Attempt)

Source IP: 203.0.113.1 (Unknown)
Count: 5,000 messages
SPF: Fail (5,000)
DKIM: Fail (5,000)
DMARC: Fail (5,000)
Policy Applied: Reject

Actions: 1. Confirm emails were rejected 2. Document incident 3. Consider abuse report to ISP


DMARC for Different Scenarios

Scenario 1: Simple Setup (Google Workspace)

# SPF
example.com. IN TXT "v=spf1 include:_spf.google.com -all"

# DKIM (configured in Google Admin)
google._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIj..."

# DMARC
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"

Scenario 2: Multiple Email Providers

# SPF
example.com. IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net -all"

# DKIM
google._domainkey.example.com. IN TXT "v=DKIM1; p=KEY1..."
em123._domainkey.example.com. IN TXT "v=DKIM1; p=KEY2..."

# DMARC
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"

Scenario 3: No Email Sent

# SPF - Reject all
example.com. IN TXT "v=spf1 -all"

# DMARC - Reject
_dmarc.example.com. IN TXT "v=DMARC1; p=reject"

Scenario 4: Subdomains with Third-Party

# Main domain - Strict
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; sp=none"

# Marketing subdomain - Separate policy
_dmarc.marketing.example.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"

DMARC and Email Forwarding

Problem: Email forwarding can break SPF alignment.

Why Forwarding Breaks DMARC

Original email:
From: user@example.com
MAIL FROM: user@example.com
SPF: Pass + Aligned ✓
DKIM: Pass + Aligned ✓
DMARC: Pass ✓

After forwarding:
From: user@example.com (unchanged)
MAIL FROM: forwarder@gmail.com (changed by Gmail)
SPF: Pass but NOT Aligned ✗
DKIM: Pass + Aligned ✓ (if not modified)
DMARC: Pass ✓ (DKIM saves it!)

DKIM alignment saves forwarded email if body/headers not modified.

Solutions

  1. Use DKIM (not just SPF)
  2. DKIM survives forwarding better
  3. Provides alignment even when SPF fails

  4. ARC (Authenticated Received Chain)

  5. Preserves authentication across forwarding
  6. Supported by major providers

  7. Relaxed alignment (default)

  8. More forgiving than strict
  9. Allows subdomain alignment

Testing DMARC

Using ReputeAPI

curl "https://api.reputeapi.com/api/v1/check?domain=example.com" \
  -H "X-API-Key: your-api-key"

Response:

{
  "dmarc": {
    "present": true,
    "valid": true,
    "record": "v=DMARC1; p=reject; rua=mailto:dmarc@example.com",
    "policy": "reject",
    "subdomain_policy": "none",
    "percentage": 100,
    "aggregate_reports": ["mailto:dmarc@example.com"]
  },
  "score": 95,
  "issues": []
}

Using Command Line

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

# Expected output:
"v=DMARC1; p=reject; rua=mailto:dmarc@example.com"

Online Tools

Send Test Email

# Send to Gmail and check authentication
echo "Test" | mail -s "DMARC Test" your-gmail@gmail.com

# In Gmail: View Original
# Look for: "DMARC: PASS"

DMARC Best Practices

1. Always Use DMARC

_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"

Even p=none provides visibility.

2. Progress Through Policies

Week 1-2:  p=none (monitor)
Week 3-6:  p=quarantine
Week 7+:   p=reject

Don't rush to reject without monitoring.

3. Set pct=100

v=DMARC1; p=reject; pct=100; rua=mailto:dmarc@example.com

Full protection, not partial.

4. Enable Aggregate Reports

rua=mailto:dmarc@example.com

Critical for monitoring and troubleshooting.

5. Use Relaxed Alignment (Default)

# Allow subdomain alignment
adkim=r; aspf=r

More compatible with email forwarding and services.

6. Protect Subdomains

# Option 1: Same policy
v=DMARC1; p=reject; sp=reject

# Option 2: Separate monitoring
v=DMARC1; p=reject; sp=none

7. Monitor Reports Regularly

  • Weekly: Check for new sources
  • Monthly: Review pass rates
  • Always: Investigate failures

8. Document Your Configuration

Keep notes on: - Policy progression timeline - Known authorized senders - Report analysis findings


DMARC Validation with ReputeAPI

Check DMARC Status

import requests

response = requests.get(
    "https://api.reputeapi.com/api/v1/check",
    params={"domain": "example.com"},
    headers={"X-API-Key": "your-api-key"}
)

result = response.json()

# Check DMARC configuration
dmarc = result['dmarc']
if not dmarc['present']:
    print("❌ No DMARC record found")
elif dmarc['policy'] == 'none':
    print("⚠️ DMARC in monitoring mode only")
elif dmarc['policy'] == 'quarantine':
    print("✓ DMARC quarantine policy active")
elif dmarc['policy'] == 'reject':
    print("✅ DMARC reject policy active (maximum protection)")

# Check for DMARC issues
dmarc_issues = [i for i in result['issues'] if i['category'] == 'dmarc']
for issue in dmarc_issues:
    print(f"\n[{issue['severity']}] {issue['message']}")
    print(f"Fix: {issue['remediation']}")
    if issue.get('dns_snippet'):
        print(f"DNS: {issue['dns_snippet']['value']}")

Monitor DMARC Policy

def check_dmarc_policy(domain):
    """Check if domain has strong DMARC policy"""
    response = requests.get(
        "https://api.reputeapi.com/api/v1/check",
        params={"domain": domain},
        headers={"X-API-Key": "your-api-key"}
    )

    dmarc = response.json()['dmarc']

    if not dmarc['present']:
        return "missing"
    elif dmarc['policy'] == 'reject' and dmarc['percentage'] == 100:
        return "excellent"
    elif dmarc['policy'] == 'quarantine':
        return "good"
    elif dmarc['policy'] == 'none':
        return "monitoring"
    else:
        return "weak"

Common Questions

Do I need both SPF and DKIM for DMARC?

No, but recommended. DMARC requires at least ONE aligned authentication: - SPF OR DKIM must pass and align - Best practice: Implement BOTH for redundancy

What if I don't receive DMARC reports?

Common reasons: 1. Wrong email format - Must be rua=mailto:email@example.com 2. Email account doesn't exist 3. Reports filtered as spam - Check spam folder 4. No email traffic - Reports only sent if email received

How long until DMARC takes effect?

Immediately after DNS propagation (usually 15min-48hrs). However: - Reports are aggregate (daily/weekly) - First report may take 24-48 hours - Full deployment takes 4-8 weeks

Can DMARC break email forwarding?

Partially. DMARC can cause issues with: - Email forwarding services - Mailing lists - Auto-forwarding rules

Solution: Use DKIM (survives forwarding better than SPF)

Should I use forensic reports?

Usually no: - Few providers send them - Privacy concerns (contain email samples) - Aggregate reports sufficient for most needs

What's a realistic timeline for p=reject?

Recommended timeline:

Weeks 1-2:  p=none (monitor)
Weeks 3-6:  p=quarantine
Weeks 7-8:  p=quarantine; pct=100
Weeks 9+:   p=reject; pct=100

Total: 2-3 months for safe rollout



API Resources