Skip to content

DMARC Enforcement Migration — From p=none to p=reject in 90 Days

Going from no DMARC to p=reject all at once breaks your own legitimate mail. The 3-stage migration (none → quarantine → reject) is the safe path. This guide walks the 90-day plan, what to monitor at each stage, when to roll back.

Why staged migration

Jumping straight to p=reject (strict enforcement) is risky:

  • If your SPF or DKIM has any misconfigurations, your own mail gets rejected
  • Forwarded mail (newsletters → mailing lists → recipient inboxes) often fails SPF — sudden rejection means lost engagement
  • Third-party services that legitimately send-on-behalf (Salesforce, Marketo, AcelleMail itself) may not be in your SPF — sudden rejection breaks integrations

The staged migration: p=none (monitor) → p=quarantine (junk folder) → p=reject (bounce). 30 days at each stage. Validate before stepping up.

Stage 1: p=none (Days 0-30)

Publish:

TXT _dmarc.yourdomain.com  "v=DMARC1; p=none; rua=mailto:[email protected]; ruf=mailto:[email protected]; fo=1; pct=100"

What happens:

  • Receivers send you aggregate reports daily (via rua=) — each contains: SPF/DKIM pass rate per source IP, per sending domain
  • Failed messages still arrive in recipient inboxes — p=none doesn't enforce, just observes
  • You discover surprises: forgotten third-party senders, mis-aligned envelope-senders, broken DKIM signing

Monitor:

  1. Read the daily DMARC aggregate reports (XML files; use a parser)
  2. In AcelleMail, watch the bounce log for any 5.7.x DSN codes (auth issues):

Bounce log — DSN reasons

  1. Check your sending-server auth chips remain green:

The auth chips on the sending-server detail

Open Settings → Sending servers → click your active server. The toolbar shows the live SPF / DKIM / DMARC chip status:

Sending server detail with auth chips

  • Green chips on all three = receiving servers can confirm you're authorized to send from this domain.
  • Any chip red = receiving servers immediately downgrade reputation. Click Verify domain in the toolbar to walk through the DNS-fix wizard.

Validate before Stage 2:

  • DMARC pass rate ≥99% in aggregate reports (last 7 days)
  • No unauthorized "spoofing" sources in aggregate (all source IPs are recognized as yours)
  • No 5.7.x bounce-spike in AcelleMail

If pass rate <99%: investigate the failing sources before proceeding.

Stage 2: p=quarantine (Days 30-60)

Update to:

TXT _dmarc.yourdomain.com  "v=DMARC1; p=quarantine; rua=mailto:[email protected]; fo=1; pct=100"

What happens:

  • Failed-DMARC messages now go to recipient's Junk folder (not Inbox)
  • Legitimate but mis-aligned messages start visibly degrading — engagement drops on poorly-authenticated sources
  • Aggregate reports continue daily

Monitor:

  1. Pass rate should stay ≥99% (your fixes from Stage 1 took effect)
  2. AcelleMail's bounce log: 5.7.x rate should be near 0%
  3. Open rate / click rate on your campaigns — should be flat (not dropping due to junk-foldering)

Validate before Stage 3:

  • 14 consecutive days at pass rate ≥99.5%
  • No customer complaints about "missing emails" routed to spam
  • Aggregate reports show NO unauthorized sources

Stage 3: p=reject (Day 60+)

Update to:

TXT _dmarc.yourdomain.com  "v=DMARC1; p=reject; rua=mailto:[email protected]; fo=1; pct=100"

What happens:

  • Failed-DMARC messages are now BOUNCED — neither inbox nor junk
  • Maximum enforcement; phishing attempts using your domain hit the wall
  • Receiving servers reject at SMTP level (visible in your bounce log if you misconfigure)

Monitor:

  1. Pass rate should remain ≥99.5%
  2. Watch for any third-party-sender issues you missed (Salesforce, Mailchimp, etc. — if they send-as-you and weren't authorized)
  3. Customer complaint volume

This is the target state. BIMI requires this stage (or quarantine minimum) before logos display.

Rollback criteria

If at any stage:

  • DMARC pass rate <99% AND you can't identify the failing source
  • Customer complaints about "emails going to spam" spike
  • Critical third-party-sender stops working

Roll back to previous stage. Update DNS to previous p= value. Wait 24-48h for cache propagation.

Diagnose the failure, fix, then re-attempt the step-up.

Reading DMARC aggregate reports

Aggregate reports arrive at your rua= address as XML (often zipped). Sample:

<feedback>
  <policy_published>
    <domain>yourdomain.com</domain>
    <p>quarantine</p>
  </policy_published>
  <record>
    <row>
      <source_ip>54.240.0.10</source_ip>
      <count>1247</count>
      <policy_evaluated>
        <disposition>none</disposition>
        <dkim>pass</dkim>
        <spf>pass</spf>
      </policy_evaluated>
    </row>
    <identifiers>
      <header_from>yourdomain.com</header_from>
    </identifiers>
    <auth_results>
      <dkim>
        <domain>yourdomain.com</domain>
        <result>pass</result>
      </dkim>
      <spf>
        <domain>yourdomain.com</domain>
        <result>pass</result>
      </spf>
    </auth_results>
  </record>
  ...
</feedback>

Parse via:

  • dmarcian.com (free tier)
  • dmarcanalyzer.com (free tier)
  • EasyDMARC (free trial + paid)
  • Postmark DMARC Digests (free; daily email digest)
  • Manual XML parse (DIY; possible but tedious)

The free / freemium tools show:

  • Per-source-IP send volume + pass rate
  • Unauthorized senders trying to use your domain (phishing attempts)
  • Per-recipient-domain breakdown (Gmail vs Outlook vs Yahoo)

Common DMARC migration issues

Symptom Likely cause Fix
Pass rate stuck at 90-95% in Stage 1 Third-party sender not in your SPF Add to SPF: include:vendor.com
Pass rate drops on weekends Weekly automation script sends from unauthorized IP Identify in aggregate report; whitelist or fix
AcelleMail sends pass; Mailchimp sends fail Mailchimp's DKIM doesn't sign with your domain Set up Mailchimp's "authenticate your domain" feature
Aggregate reports say "DKIM domain mismatch" DKIM signing different domain than From: header Ensure consistent domain alignment
Forwarded mail through old mailing-list fails Forwarders break SPF Move to DKIM-aligned sending OR set up ARC at the forwarder
Customer complaint: legitimate email in spam Premature p=quarantine step Roll back to p=none; investigate; resume

What pct= does

pct=50 applies the policy to 50% of failing messages, lets 50% through. Useful for gradual rollout within a stage:

Week 1 of p=quarantine: pct=10  (10% to junk, 90% to inbox)
Week 2: pct=25
Week 3: pct=50
Week 4: pct=100  (full quarantine)

If problems surface, dial back without losing the staged-rollout progress.

Advanced: subdomain DMARC policies + ARC for forwarding + multi-tenant DMARC at scale

Subdomain DMARC policies:

The apex _dmarc.brand.com record applies to ALL subdomains by default. To override per-subdomain:

TXT _dmarc.brand.com         "v=DMARC1; p=quarantine; sp=reject; ..."
                                                    ^^^^^^^^^^
                                                    Subdomain policy

The sp= parameter sets the subdomain policy. Useful for strict subdomain enforcement (transactional subdomain) while keeping the apex at quarantine.

For explicit per-subdomain DMARC:

TXT _dmarc.mail.brand.com  "v=DMARC1; p=quarantine"

Overrides whatever the apex DMARC says for mail.brand.com specifically.

ARC (Authenticated Received Chain) for forwarders:

When email is forwarded (e.g. recipient's filter forwards to another address), the original SPF + DKIM can break. ARC records the original auth state so the final receiver knows it was legit at the original hop.

ARC-Authentication-Results: i=1; ...
ARC-Seal: i=1; a=rsa-sha256; ...
ARC-Message-Signature: ...

If you operate a mailing list / forwarder, configure ARC on your relay. AcelleMail signs ARC automatically when relevant.

Multi-tenant DMARC at scale:

For SaaS platforms sending on behalf of many customers, each customer's domain needs its own DMARC. The platform doesn't manage customer DMARC directly — customers do — but the platform must:

  1. Provide each customer with proper SPF includes
  2. Ensure DKIM signing uses customer's domain
  3. Document the staged migration process (this article!) for customer guidance

Some platforms offer "managed DMARC" — paid feature where the platform monitors customer DMARC reports and adjusts policies.

DMARC monitoring automation:

#!/bin/bash
# Daily aggregate report processor
# Pulls DMARC reports from inbox, parses, aggregates pass-rate

mailbox_check_dmarc_reports
parse_xml_reports
calculate_pass_rate_last_24h

if [ $pass_rate -lt 99 ]; then
  notify_slack "DMARC pass rate dropped to $pass_rate% — investigate"
fi

if [ $pass_rate -ge 99 ] && [ "$current_policy" = "none" ] && [ $days_at_stage -ge 30 ]; then
  notify_slack "Stage 1 complete — ready to step up to p=quarantine"
fi

Phasing in pct= for ultra-conservative rollout:

Week 1: pct=5
Week 2: pct=10
Week 3: pct=25
Week 4: pct=50
Week 5: pct=75
Week 6: pct=100

12-week journey from p=none → p=reject with maximum conservatism. Reasonable for risk-averse industries (financial services, healthcare).

DMARC SaaS platforms:

If managing DMARC across 5+ domains:

  • EasyDMARC — comprehensive, $50-200/month
  • DMARCLY — budget-friendly, $20-100/month
  • Red Sift OnDMARC — enterprise, $200+/month
  • Postmark DMARC Digests — free; daily email summary

Most provide one-pane visibility + automated alerts + recommendations.

Related articles

16 comments

11 comments

  1. Daniel
    The SPF flattening explanation finally made it click for me. I'd been hitting the 10-lookup limit and didn't understand why nesting includes counted.
  2. Vera
    Worth noting: our DNS provider (Cloudflare) caches negative responses for 1 hour. We added a TXT record, dig showed it, but mail-tester said missing for another 40 minutes. Almost lost our minds. TTL was set to 300 but the parent zone NS cache held
    1. Admin
      This bites people every DMARC migration and it's worth spelling out: the 300 you set on the TXT record never applied, because there was no record to cache. What got cached was the *absence* of `_dmarc.yourdomain.com`, and negative caching is governed by the SOA minimum of the zone, not by your record's TTL. Cloudflare's SOA minimum lines up with the hour you waited. Practical order of operations, which I'll fold into the section on publishing the p=none record: create the `_dmarc` TXT record *before* you run any checker against it. Once a validator has queried and got NXDOMAIN, you're waiting out that resolver's negative cache and there's nothing you can do from your side. `dig` against 1.1.1.1 or 8.8.8.8 directly will show it, mail-tester is going through its own resolver with its own cache, which is why the two disagreed for 40 minutes. If you need to confirm without waiting, query your provider's authoritative nameservers directly: `dig TXT _dmarc.example.com @ns1.yourprovider.com`. Authoritative answers skip the cache entirely and tell you whether the record is actually live.
  3. Isabella
    Easy win: set up dmarcian.com (free tier) to receive your DMARC RUA reports. The first 2 weeks of reports tell you everything you didnt know about who's sending as you.
  4. Minh
    Thanks for the explicit cautionary tales. The alignment-vs-pass distinction is exactly where I lost a week last year.
    1. Admin
      That distinction eats a lot of people, because every tool you look at reports SPF: pass and you assume you're done. A pass on the return-path domain tells you nothing about whether it aligns with the From: header, and DMARC only cares about the second thing. Third-party senders are where it bites hardest, since they bounce off their own envelope domain by default and you have to go set up a custom return-path before anything aligns. The tell is in the aggregate reports: look at the policy_evaluated block rather than the auth_results block. auth_results will happily show pass while policy_evaluated shows fail for the same message. If you'd seen those side by side you'd probably have found it in an hour instead of a week.
  5. Hùng
    Hit the 10-lookup SPF limit when we tried to layer SES on top of an existing Google Workspace setup. Flattened with a tool (spfwizard.com) and it's been fine since. That tool's worth a mention...
  6. Joel
    How do you handle DNS for clients in white-label setups? The customer would need to add records to their domain — is there a clean way to bulk-verify those?
    1. Admin
      There's no bulk DMARC verifier in the product today, and I'd be careful about promising one, because DMARC is per-org-domain while the records you actually hand a white-label customer are usually the SPF include, the DKIM selector, and a CNAME for the tracking/bounce domain. Those three we do check per sending identity, and you can re-run the check from the identity page after they've published. What people in your position tend to do is script it: a loop over the customer domains doing a TXT lookup on _dmarc.<domain>, then flag anything still on p=none past whatever day of the 90 you're on. That's about 20 lines of shell with dig, and it works fine because DMARC lives on the customer's org domain, not on anything we control. The part worth building is a per-customer readiness panel that shows SPF/DKIM/DMARC state in one row so support isn't doing lookups by hand. That's on the list, no date. If you write the polling script anyway, send it over and I'll fold it into the article as an appendix with your name on it.
  7. Linh
    whats your recommendation for sub-domains? We send from mail.example.com AND notifications.example.com. Same DKIM selector or separate?
    1. Admin
      Separate selectors. There's no benefit to sharing one, and a lot of downside: if you ever need to rotate a key because notifications.example.com got compromised or a vendor leaked it, a shared selector means you're rotating mail.example.com at the same time and taking the deliverability hit on both. Use something you can read at a glance in the DNS zone, like sel-mail and sel-notif, or just date them (2026q3-mail). Two things that catch people out on this article's timeline: Each subdomain needs its own DKIM record at selector._domainkey.mail.example.com. The parent's key does not cover it. Subdomains inherit the org domain's DMARC policy unless you publish a record for them. So once you go p=reject at example.com, both subdomains are enforced too, whether or not you were watching their aggregate reports. If one of them isn't ready, use sp=none on the parent while you fix it, then drop the sp tag. Don't leave sp=none in place permanently, it's a hole. If you're at the p=none stage now, check your RUA reports are actually broken out per subdomain before you move to quarantine. Most report parsers show the header From domain, so a subdomain failing alignment is easy to miss in the aggregate view.
  8. Aisha
    quick question: do receivers actually enforce the SPF -all hard fail, or do most just downrate? I've heard mixed things and I'm hesitant to switch from ~all
    1. Admin
      Mixed things is about right, because the behaviour genuinely varies. Some receivers do reject on -all outright, but the big mailbox providers mostly treat SPF failure as one signal and let DMARC make the final call. That's why the article pushes DMARC policy as the lever rather than the SPF qualifier. Practical take: if you're at p=none and still reading reports, the -all vs ~all choice barely changes what lands, because your DMARC policy isn't asking anyone to reject anything yet. The thing that actually bites with -all is forwarding and mailing lists, where SPF breaks by design and you're leaning on DKIM to keep alignment. So get DKIM signing solid and aligned first, confirm it in the aggregate reports, then tighten SPF. Doing it in that order means -all costs you nothing you weren't already going to lose at p=reject. If you want to stay on ~all forever that's a defensible position too. Enforcement lives in the DMARC record.
  9. Priya
    If you use Vercel or Netlify for the apex, watch out — they sometimes override TXT records via their auto-DNS feature. Bit us once with a stripped SPF record
  10. Lucas
    dns setup is one of those things where you don't know what you don't know. this article should be required reading for anyone running their own mail.
  11. Yuki
    Our DKIM rotation broke for 2 days because we updated the active selector first, then waited to delete the old. Should be the other way — publish new, wait 48h for cache, switch sending, THEN remove old.

More in DNS & Domain Setup