Configuring a DMARC Policy: Moving Safely from p=none to p=reject
Stage a DMARC rollout on a live transactional domain: p=none monitoring, pct-ramped quarantine, then reject, with exact DNS records and aggregate-report reading.
You run a live domain already sending password resets, receipts, and notifications, and you need to move DMARC from monitoring to full p=reject enforcement without bouncing a single legitimate message. The danger is that flipping straight to reject rejects mail you forgot you were sending. This walks the staged rollout that prevents that, building on the records introduced in the SPF, DKIM, and DMARC authentication guide.
Root Cause: Why Jumping Straight to Reject Breaks Legitimate Mail
DMARC enforcement acts on every message claiming your From domain, including senders you may have forgotten. Two categories fail at the moment you publish p=reject:
- Unaligned forwarders. When a recipient auto-forwards your mail, the forwarding server becomes the new connecting IP, so SPF breaks. DKIM survives forwarding only if the forwarder does not alter the body. A forwarder that appends a footer breaks the DKIM body hash too, leaving the message with no aligned, passing mechanism — and
rejectbounces it. - Third-party senders. Your billing platform, helpdesk, CRM, and marketing tool may all send as your domain without aligned SPF or DKIM. Under
p=nonethey deliver fine; underp=rejectthey vanish.
The entire point of the staged rollout is to discover these senders through aggregate reports before enforcement can drop their mail.
There is a second, subtler failure mode: alignment that passes in isolation but not for the From domain. A message can show spf=pass and dkim=pass in the raw headers yet still fail DMARC, because the passing SPF was for the ESP's bounce domain and the DKIM d= was a provider subdomain — neither aligned with the visible From. At p=none this is invisible because nothing acts on the failure; at p=reject it bounces real mail. Aggregate reports expose exactly this distinction, separating "authenticated" from "authenticated and aligned," which is why you must read the reports rather than assume a passing test send means you are safe to enforce.
Exact Fix: The Staged Rollout
Stage 1 — Monitor with p=none
Publish a monitoring-only policy. It changes nothing about delivery; it only asks receivers to send you reports.
; Stage 1: monitoring only. Delivery is unaffected; collect reports.
_dmarc.yourdomain.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-agg@yourdomain.com; ruf=mailto:dmarc-forensic@yourdomain.com; fo=1; adkim=s; aspf=s; pct=100"
; | | | | |
; | | | | +-- fo=1: report on any auth failure (not just full DMARC fail)
; | | | +-- forensic reports; few receivers send, but harmless to request
; | | +-- aggregate XML rollups; Google, Yahoo, Microsoft send these daily
; | +-- no action taken on failures yet
; +-- required first token
Leave this in place for at least two to four weeks so weekly and monthly senders appear in the data.
Stage 2 — Quarantine with a pct Ramp
Once reports confirm your legitimate senders pass, move to quarantine but only on a fraction of mail using pct. Failing mail lands in spam rather than bouncing, so a mistake is recoverable.
; Stage 2a: quarantine 25% of failing mail. Failures go to Junk, not bounced.
_dmarc.yourdomain.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc-agg@yourdomain.com; adkim=s; aspf=s; pct=25"
; | |
; | +-- only 25% of failing mail is quarantined; rest still delivered
; +-- failing mail to Gmail/Yahoo/Outlook is filed as spam
Ramp pct=25 → 50 → 100 over one to two weeks each, watching aggregate reports for any drop in legitimate volume.
Stage 3 — Reject
With pct=100 quarantine clean, switch to full enforcement.
; Stage 3: full enforcement. Unaligned mail is bounced by Gmail, Yahoo, Outlook.
_dmarc.yourdomain.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc-agg@yourdomain.com; adkim=s; aspf=s; pct=100; sp=reject"
; | |
; | +-- subdomain policy: reject spoofed mail from unused subdomains
; +-- receivers bounce any message failing DMARC
Drop the ruf=/fo= once stable — forensic reports carry recipient data and few receivers send them.
How to Read Aggregate Reports
Receivers send daily gzipped XML to your rua address. Each <record> reports a source IP, message count, and the SPF/DKIM/DMARC result. The fields that matter:
<record>
<row>
<source_ip>198.51.100.7</source_ip> <!-- which server sent as your domain -->
<count>240</count> <!-- how many messages -->
<policy_evaluated><disposition>none</disposition></policy_evaluated>
</row>
<auth_results>
<spf><domain>yourdomain.com</domain><result>pass</result></spf> <!-- aligned SPF -->
<dkim><domain>yourdomain.com</domain><result>pass</result></dkim> <!-- aligned DKIM -->
</auth_results>
</record>
Reverse-lookup unfamiliar source IPs. A source passing for mail.helpdesk.com instead of yourdomain.com is an unaligned third party you must fix before enforcement. Hand-parsing XML does not scale; route reports to a processor (Dmarcian, Postmark's DMARC Digests, or a self-hosted parser) and feed the parsed signal into your webhook event pipelines for alerting.
Variant Cases
Subdomain policy (sp=). The p= policy applies to the organizational domain; sp= governs subdomains. Set sp=reject to stop attackers spoofing unused subdomains like marketing.yourdomain.com. If a subdomain has its own slower rollout, give it a dedicated _dmarc.subdomain record with its own p=, which overrides the parent's sp.
Multiple third-party senders. Each tool needs either an aligned DKIM signature (d=yourdomain.com) or an aligned custom Return-Path. The cleanest pattern is to delegate each sender to its own subdomain — bills.yourdomain.com for billing, notify.yourdomain.com for the ESP — so reputation is isolated and alignment is straightforward. Your ESP selection and integration provider supplies the custom Return-Path needed for SPF alignment.
Aggregate-Report Tooling
Hand-parsing gzipped XML stops scaling after the first week — a single domain can receive dozens of reports a day from Google, Yahoo, Microsoft, Comcast, and others, each with many <record> rows. Route the rua mailbox to a processor that aggregates across receivers and surfaces unaligned sources as a list rather than raw XML. The realistic options:
| Tool | Hosting | Best for |
|---|---|---|
| Dmarcian | SaaS | Rich source-IP attribution and rollout dashboards |
| Postmark DMARC Digests | SaaS (free) | Weekly plain-language digest; good for low-volume domains |
| Valimail / EasyDMARC | SaaS | Automated source identification and guided enforcement |
parsedmarc + Elasticsearch/Grafana |
Self-hosted | Full data ownership; EU residency; custom alerting |
For a self-hosted pipeline, parsedmarc ingests the rua mailbox over IMAP, parses each report, and writes structured records you can query. The key field to alert on is any source where the message count is non-trivial and neither SPF nor DKIM aligns with your organizational domain — that is an unaligned legitimate sender (or an active spoofer) you must resolve before tightening the policy.
# parsedmarc: pull DMARC aggregate reports from the rua IMAP mailbox and index them.
# Run on a schedule; the output feeds a dashboard that flags unaligned sources.
parsedmarc --imap-host imap.yourdomain.com \
--imap-user dmarc-agg@yourdomain.com \
--elasticsearch-hosts http://localhost:9200 \
--save-aggregate
# Alert rule (pseudo): disposition trending toward quarantine/reject for a KNOWN source = a sender about to break at enforcement.
The decisive metric the tooling exposes is the split between "authenticated" and "authenticated and aligned." A raw test send can show spf=pass/dkim=pass yet still be unaligned; only the aggregate data, summed across receivers and source IPs, tells you whether you are actually safe to advance a stage.
Variant: Aligning a Stubborn Third-Party Sender
The hardest senders to bring into alignment are SaaS tools that send "as" your domain — billing (Stripe, Chargebee), helpdesk (Zendesk, Intercom), CRM, and survey tools — because many sign DKIM with their own d= domain and use their own Return-Path, so they pass authentication but never align with your From. You have three levers, in order of preference:
- Custom DKIM (
d=yourdomain.com). The cleanest fix: the vendor provides CNAME records that delegate a signing selector to your domain, so thed=becomesyourdomain.comand DKIM aligns. Most mature SaaS senders support this — enable it everywhere it exists. - Custom Return-Path / MAIL FROM. If the vendor cannot do custom DKIM but lets you set a custom envelope domain (e.g.
bounce.yourdomain.com), SPF then aligns through that subdomain. This depends on the ESP and provider integration settings the vendor exposes. - Dedicated subdomain with its own policy. When a sender supports neither, move it to a dedicated subdomain (
survey.yourdomain.com) with its own_dmarcrecord kept atp=noneorp=quarantinewhile the parent goes toreject. This isolates the laggard so it cannot block enforcement on the organizational domain, and its weaker policy is scoped to a subdomain attackers rarely target.
Drive each sender from the aggregate-report data: the report tells you exactly which source IP and d=/Return-Path a tool is using, which is what you reference when you open a support ticket asking the vendor to enable aligned DKIM.
Pipeline Integration: Managing the Record as Code
Hand-editing DNS in a console invites typos and undocumented changes. Manage the DMARC record in your DNS-as-code so each stage of the rollout is a reviewed commit.
# Terraform: DMARC record managed in version control (AWS Route 53).
# Promoting through stages is a reviewed pull request, not a console edit.
resource "aws_route53_record" "dmarc" {
zone_id = aws_route53_zone.primary.zone_id
name = "_dmarc.yourdomain.com"
type = "TXT"
ttl = 300 # short TTL during rollout so policy changes propagate fast
records = [
# Bump p and pct here per stage; git history is your rollout audit log.
"v=DMARC1; p=quarantine; rua=mailto:dmarc-agg@yourdomain.com; adkim=s; aspf=s; pct=50; sp=reject"
]
}
Keep the TTL low (300s) during the rollout so a regression can be rolled back fast, then raise it once you stabilize at p=reject. Encode the rollout itself as a sequence of merged pull requests — one per stage — so the git history doubles as an audit trail of when each policy change went live and who approved it. Pair each promotion with a note in the PR description recording the aggregate-report state that justified it ("two weeks at quarantine pct=100, zero unaligned legitimate sources"), so a future engineer can reconstruct why the domain is at its current policy without spelunking through receiver reports.
A practical guardrail: gate the Terraform apply behind the same lookup-count and policy-sanity checks you run for SPF, so a typo like p=rejct or a record that would exceed DNS length limits fails CI rather than silently disabling protection in production.
Validation Checklist
p=nonehas run long enough to capture weekly and monthly senders (2-4 weeks minimum).- Every passing source IP in aggregate reports is a known, intended sender.
- All legitimate third-party senders show aligned SPF or DKIM before any enforcement.
pctramped25 → 50 → 100at quarantine with no drop in legitimate volume.sp=is set to protect unused subdomains.- The record is managed in DNS-as-code with a short TTL during rollout.
- A post-
rejecttest from a forwarder and each third-party tool still reaches the inbox.
Related
- SPF, DKIM, and DMARC for transactional email — the full authentication layer this rollout sits on
- Fixing SPF PermError: too many DNS lookups — an SPF PermError can sink DMARC alignment during rollout
- ESP selection and integration — configuring a custom Return-Path for SPF alignment
- Webhook event pipelines — routing parsed aggregate reports into alerting