Module 5 · Lesson 5

DMARC: Domain-Based Message Authentication

16 min read

dmarcemail-authenticationspfdkimreportingdeliverability

DMARC: Domain-Based Message Authentication

SPF says which IPs can send as your domain. DKIM says which messages were cryptographically signed by your domain. But neither one tells receiving servers what to do when those checks fail. That's DMARC.

DMARC is a TXT record that publishes your policy: if a message fails SPF or DKIM, should the receiving server pass it, quarantine it (move to spam), or reject it outright? It also sets up reporting so you get data on everything being sent as your domain, legitimate or not.

The Record

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

DMARC records always go at _dmarc.<yourdomain>, not the domain root. The subdomain prefix is mandatory.

Verify yours:

dig TXT _dmarc.example.com +short

Record Syntax

A production DMARC record looks like this:

v=DMARC1; p=reject; rua=mailto:dmarc-agg@example.com; ruf=mailto:dmarc-forensic@example.com; adkim=r; aspf=r; sp=reject; pct=100

Breaking down each tag:

v=DMARC1 — Required. Identifies this as a DMARC record.

p= — Policy for the organizational domain. Three options:

  • none — Monitor only. No action taken on failures.
  • quarantine — Move failing messages to spam/junk.
  • reject — Reject failing messages entirely (550 SMTP response).

rua= — Aggregate report URI. Where to send daily aggregate reports. Takes mailto: or https: URIs. Accepts multiple, comma-separated.

ruf= — Forensic report URI. Copies of failing messages (redacted). Note: fewer providers send these due to privacy concerns. Don't rely on ruf as your primary signal.

adkim= — DKIM alignment mode. r = relaxed (subdomains match), s = strict (exact domain match only). Default: relaxed.

aspf= — SPF alignment mode. Same options as adkim. Default: relaxed.

sp= — Subdomain policy. Separate policy for subdomains. If omitted, subdomains inherit p=.

pct= — Percentage of messages to apply the policy to. pct=10 means apply the policy to 10% of failing messages, pass the rest. Used for gradual rollout. Default: 100.

Alignment: What It Actually Means

DMARC doesn't just check that SPF or DKIM passed. It checks that they passed and that the authenticated domain aligns with the From: header domain. This is the piece that actually prevents spoofing.

SPF alignment: The domain in MAIL FROM (envelope sender) must match the From: header domain.

  • Relaxed: subdomains match. mail.example.com aligns with example.com.
  • Strict: exact match required.

DKIM alignment: The d= value in the DKIM-Signature must match the From: header domain.

  • Relaxed: d=mail.example.com aligns with From: user@example.com.
  • Strict: d= must exactly match the From domain.

For DMARC to pass, at least one of SPF or DKIM must pass and be aligned. That's the key: passing SPF or DKIM from a completely different domain doesn't count.

The Path to p=reject

This is the sequence. Don't skip steps.

Step 1: Publish p=none and collect data

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

Run this for at least two weeks, ideally a month. Every legitimate mail source needs to show up in your reports before you enforce anything.

Step 2: Read the aggregate reports

Aggregate reports are XML files. Raw XML is unpleasant. Use a tool:

  • Dmarcian — most detailed, great visualization, free tier exists
  • MXToolbox — simpler, free report parser
  • Google Postmaster Tools — useful if you send to Gmail at scale
  • URIports — free tier, good for smaller domains

What you're looking for:

  • Which IP addresses are sending as your domain?
  • Are they all passing SPF and DKIM?
  • Are there sources you don't recognize?

Common discoveries during p=none monitoring:

  • Transactional email service you forgot to add to SPF
  • CRM tool that sends notifications from your domain
  • Old Mailchimp integration that was never cleaned up
  • Actual phishing attempts from random IPs

For each source that's failing: either add it to SPF/set it up with DKIM, or confirm it shouldn't be sending as your domain.

Step 3: Fix failures

For each legitimate sender that's failing:

  • Add their sending IPs to your SPF record
  • Set up DKIM signing through their interface
  • Verify alignment (their From header should match your domain)

For SendGrid as an example:

  • Add include:sendgrid.net to your SPF
  • Generate a DKIM key through SendGrid's interface, publish the TXT record they provide
  • Their From domain should be your domain, not a sendgrid.com subdomain

Step 4: Move to p=quarantine with pct rollout

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

Start at 10%. Failing messages from unauthorized senders go to spam for 10% of recipients, pass for 90%. Monitor the aggregate reports for unexpected failures. If you see legitimate mail starting to fail, you missed something in step 3.

Increase pct gradually: 10 → 25 → 50 → 100.

Step 5: p=reject

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

At this point, unauthorized messages sent as your domain are rejected at the SMTP level. The sp=reject covers subdomains.

This is the goal. p=reject with DKIM and SPF properly configured is what gives your domain strong email authentication posture.

What Aggregate Reports Contain

A single aggregate report covers a 24-hour period and comes from each receiving provider that saw email claiming to be from your domain. Each report is an XML file with this structure:

<feedback>
  <report_metadata>
    <org_name>Google, Inc.</org_name>
    <date_range>
      <begin>1709251200</begin>
      <end>1709337600</end>
    </date_range>
  </report_metadata>
  <policy_published>
    <domain>example.com</domain>
    <p>none</p>
    <adkim>r</adkim>
    <aspf>r</aspf>
  </policy_published>
  <record>
    <row>
      <source_ip>203.0.113.10</source_ip>
      <count>15</count>
      <policy_evaluated>
        <disposition>none</disposition>
        <dkim>pass</dkim>
        <spf>pass</spf>
      </policy_evaluated>
    </row>
    <identifiers>
      <header_from>example.com</header_from>
    </identifiers>
    <auth_results>
      <dkim>
        <domain>example.com</domain>
        <result>pass</result>
        <selector>mail2024</selector>
      </dkim>
      <spf>
        <domain>example.com</domain>
        <result>pass</result>
      </spf>
    </auth_results>
  </record>
</feedback>

Each <record> represents a unique sending IP. The <count> is how many messages came from that IP during the period. <dkim> and <spf> show whether they passed. When you're in p=none, <disposition>none</disposition> means no action was taken even if they failed.

When you see a source_ip with dkim=fail and spf=fail, that's either a misconfigured legitimate sender or someone spoofing your domain. Track it down.

Key Takeaways

  • DMARC goes at _dmarc.<yourdomain> as a TXT record.
  • p=none monitors, p=quarantine goes to spam, p=reject blocks entirely.
  • DMARC requires alignment: SPF/DKIM must pass AND the authenticated domain must match the From header.
  • Start at p=none, read aggregate reports for 2-4 weeks, fix failures, then advance to quarantine → reject.
  • pct= lets you roll out enforcement gradually.
  • rua= gives you aggregate reports. Use a DMARC analysis tool. Raw XML is not fun.

Further Reading

  • RFC 7489 — Domain-based Message Authentication, Reporting, and Conformance (DMARC)
  • dmarcian.com — DMARC management and report analysis
  • mxtoolbox.com/dmarc.aspx — check your DMARC record
  • dmarc.org/resources/deployment-guide/ — official deployment guide

Up Next

Lesson 06 covers BIMI: once you have p=reject, you can display your logo in Gmail, Apple Mail, and Yahoo Mail. The requirements, the certificate cost, and an honest assessment of whether it's worth the effort.