Introduction
Publishing a DMARC record is deceptively simple. The TXT record is short, the syntax is documented in RFC 7489, and the major mail platforms all have one-click setup helpers. What's not simple is publishing the right record — one that protects the domain without breaking legitimate mail, scales as the sender inventory changes, and reports the right data to the right destination. That's the difference between "we set up DMARC" and "DMARC is actually doing what it's supposed to do."
This article is the practical companion to the DMARC for MSPs pillar and the conceptual reference for the DMARC Record Generator tool. It walks each tag in order, explains the safe default for new deployments, and maps the rollout path from monitoring-only to full enforcement.
Why this topic matters
Most DMARC engagements start with the question "what should the record look like for a client we're onboarding tomorrow?" The honest answer is: it depends on what they're sending today, what they want to be sending in six months, where their reports should go, and how aggressive they want to be about enforcement. None of those questions have a one-size-fits-all answer.
The MSPs that get this right have a default template they start from, a clear set of decisions they walk the client through, and a tool that composes the resulting record without typos. That combination — template + decisions + tool — turns "compose a DMARC record" from a 30-minute task into a 5-minute conversation.
The anatomy of a DMARC TXT record
A DMARC record is a single TXT record published at _dmarc.example.com. Its content is a string of tag=value pairs separated by semicolons. RFC 7489 defines roughly a dozen tags; in practice four or five are common, three or four more show up occasionally, and the rest are edge cases.
The canonical shape:
“ v=DMARC1; p=quarantine; pct=25; rua=mailto:[email protected]; ruf=mailto:[email protected]; adkim=r; aspf=r; fo=1; ri=86400 “
The tags, in the order the DMARC Record Generator tool composes them:
v=DMARC1. The version tag. Always literally v=DMARC1 and must be the first tag in the record. Any other version makes the record invalid. No decision required.
p= (policy). The receiver-side enforcement instruction for the apex domain. Three values: none, quarantine, reject. Decision required; depends on rollout stage.
sp= (subdomain policy). Same values as p=; applies to sub-domains explicitly. Omit to inherit the apex policy (default behavior).
pct= (percentage). Applies the policy to a percentage of failing mail; the rest is treated as p=none. Omit when not in a rollout phase; default is 100.
rua= (aggregate reports). One or more mailto: addresses where the receiver should send aggregate reports. Comma-separated list. Decision required.
ruf= (forensic reports). One or more mailto: addresses where the receiver should send individual failure reports. Comma-separated list. Optional; many receivers don't send these.
adkim= (DKIM alignment). r for relaxed (default) or s for strict. Omit to inherit the default.
aspf= (SPF alignment). r for relaxed (default) or s for strict. Omit to inherit the default.
fo= (failure-report options). Controls when ruf= reports are generated. 0 (default, report only on full DMARC fail), 1 (report on any auth fail), d (DKIM fail), s (SPF fail). Omit when not actively investigating.
ri= (report interval). Aggregate report interval in seconds. Default 86400 (24 hours). Omit unless you have a reason to deviate.
That's the whole vocabulary. Knowing it cold lets you skim any DMARC record and read its intent immediately.
The safe default for new deployments
Every MSP DMARC practice should have a starter template. The one that holds up:
“ v=DMARC1; p=none; rua=mailto:<tenant>@<dmarc-platform> “
Three tags, one of which is the version, the other two are the minimum that produces useful behavior. The reasoning:
p=none because you don't yet know which legitimate senders are misconfigured. p=none gives you visibility without enforcement, which is exactly what the first 30-60 days of a deployment need.
rua= because without aggregate reports you can't see what's happening. The whole point of starting at p=none is to collect reports; if you don't have a destination, you're publishing a useless record.
Skip the rest until you have a reason to add them. pct= adds complexity to early reports for no benefit at p=none. sp= duplicates the apex policy needlessly. adkim/aspf default to relaxed, which is correct for almost everyone. fo= and ri= are tuning knobs you don't need until you understand what you're tuning.
The DMARC Record Generator tool defaults to this exact shape. Drop in the domain and the reporting address, copy the result, publish, and you're set up correctly for the first 60 days.
The rollout path: from p=none to p=reject
The whole point of starting at p=none is to eventually leave it. The rollout path that holds up across most clients:
Phase 1: Monitoring (p=none, 30-60 days). Aggregate reports flow in. You build the sender inventory — every legitimate platform sending mail on the client's behalf. You authenticate each one (SPF includes, DKIM keys, alignment fixes). By the end of the phase, your DMARC pass rate against legitimate mail should be above 95%.
Phase 2: Partial enforcement (p=quarantine, pct=25, then 50, then 75, 30-60 days). Quarantine sends failing mail to spam folders rather than rejecting it outright. Starting at pct=25 means only a quarter of failing mail is quarantined; the rest stays at p=none equivalent. Increment pct as you gain confidence. The whole phase is 30-60 days.
Phase 3: Full quarantine (p=quarantine, pct=100, 30-60 days). All failing mail goes to spam. Reports should be quiet — if there's a spike in failures, investigate before moving on.
Phase 4: Full enforcement (p=reject, pct=100). Failing mail is rejected at the SMTP layer. Spoofs bounce. The DMARC engagement is doing its job. Steady-state operation begins.
The total elapsed time from p=none to p=reject for a healthy client is 90-150 days. Some clients (complex senders, multiple acquisitions, weak DNS hygiene) take longer. Some clients (a single domain, M365 only, no third-party senders) can compress to 30-60 days total. Don't compress beyond what the reports tell you is safe.
The DMARC Record Generator tool makes the phase transitions a single-click change — bump the policy, optionally adjust pct, copy the new record, publish. The whole transition is a 5-minute task once you've decided to make it.
sp= and the sub-domain question
The sp= tag is where many DMARC records go wrong. Most domains have sub-domains the apex policy needs to think about — marketing.example.com for the marketing platform's sending domain, mail.example.com for the SaaS app, notify.example.com for transactional. Without sp=, the apex policy applies to sub-domains by default.
That's usually fine, but two cases where you'd want to override:
More permissive sub-domain policy. If the apex is at p=reject but a specific sub-domain is still being onboarded, you can publish a separate _dmarc.<subdomain> record at p=quarantine or p=none for the sub-domain. The apex policy applies until the sub-domain's own record overrides it.
Tighter sub-domain policy. If the apex is at p=quarantine and you want to be especially aggressive about a sensitive sub-domain (executive comms, finance), publish a sub-domain DMARC record at p=reject. The apex stays at quarantine; the sub-domain is locked down.
For most domains, sp= at the apex isn't needed — the apex policy inherits to sub-domains, and where overrides are needed, publishing a sub-domain DMARC record is cleaner than expressing the override at the apex. The DMARC Record Generator tool lets you set sp= explicitly when you need it, but the default is "inherit from p=" which is the right answer most of the time.
Reporting destinations: where rua= should point
The rua= tag is where the receivers send aggregate reports. There are three sane patterns:
Tenant address at your DMARC platform. The most common shape for managed-service clients. Each tenant has a unique address (<tenant-id>@reports.dmarc-platform.example.com); the platform routes the inbound XML to the correct tenant's processing pipeline. The MSP sees the parsed reports in the dashboard; the client sees a clean tenant view.
Direct mailbox you operate. For one-off engagements or small deployments, a mailbox you own can collect the raw XML. You parse it manually or with a script. Cheap but doesn't scale — aggregate reports are tens of megabytes per month for a busy domain.
Mixed routing. Multiple rua= addresses in the same record. Receivers send the report to all of them. Useful when transitioning between platforms or running a dual-track verification setup. Comma-separated list in the tag.
Some rules of the rua= tag:
- The address must be a
mailto:URI. Bare email addresses without the prefix are technically invalid. - The receiving mailbox must accept mail from any sender — the receivers can't authenticate to you before sending the report.
- If the rua= mailbox is at a different domain than the published policy, the receiving domain must explicitly authorize the policy domain via a
_report._dmarc.<rua-domain>TXT record. (This catches everyone the first time.) - Don't publish ruf= without rua=. Forensic reports are individual failure samples; aggregate reports are the summary. Without aggregate reports, the forensic stream is uncontextualized noise.
The DMARC Record Generator tool validates each rua= address as you type and flags the cross-domain authorization case so you don't publish a record that silently drops reports.
Common mistakes the generator catches
A short list of the patterns that show up in real misconfigurations:
Jumping straight to p=reject without a monitoring phase. Some domains can do this safely (clean sender inventory, single platform, no acquisitions). Most can't. Skipping monitoring means you discover legitimate senders that aren't authenticated by bouncing their mail, which is the worst possible time to find out.
Wrong rua= format. Multiple addresses without commas, missing mailto: prefix, plain text addresses with whitespace. Receivers silently drop reports they can't parse.
Invalid pct values. pct=0 is technically valid but means "apply the policy to nothing", which is functionally p=none. pct=101 (or any value outside 0-100) is invalid. The generator clamps these.
ri= set too low. ri=3600 (one-hour intervals) generates an order of magnitude more reports than ri=86400 (daily). Most platforms can't ingest the hourly volume cleanly; daily is the standard.
Stale fo=1 from a long-resolved investigation. fo=1 generates forensic reports on every auth failure. Useful during an active investigation; expensive and noisy as a steady state. Audit fo= values annually.
Apex policy at p=none and assuming sub-domains inherit. They do, but if a sub-domain's senders aren't authenticated, you'll still see DMARC failures in reports for the sub-domain. Not a bug — but the reports tell you something different than you might expect on first read.
When to publish strict alignment
The adkim=s and aspf=s tags switch alignment from relaxed (the default) to strict. Strict alignment means the From: domain must match the SPF / DKIM authenticated domain exactly — sub-domain matches don't count.
The honest answer for most clients: leave both at relaxed. Strict alignment catches a small number of additional edge cases at the cost of breaking legitimate mail patterns that include slight sub-domain variation. The cost-benefit usually favors relaxed.
Cases where strict makes sense:
- High-value targets (financial services, regulated industries) where the marginal protection is worth the operational friction.
- Domains with very controlled sender inventories (a single platform, no marketing tooling, no third-party SaaS) where the legitimate mail will always align strictly anyway.
- Domains that have been at relaxed for a year and want to tighten one more notch as part of a posture review.
For everyone else, relaxed is the right default. The generator defaults to relaxed and omits adkim= / aspf= from the published record, which is cleaner than publishing the default explicitly.
Verifying the published record
Once the record is composed and published, verification is two steps:
Step 1: Confirm the published record matches the intent. Query _dmarc.<domain> from at least two recursive resolvers using the DNS Lookup tool. The TXT record should match the composed text exactly. If it doesn't, the DNS publishing step went wrong (escaping issues, character encoding, line-length problems).
Step 2: Confirm reports start flowing. Within 24-48 hours of publishing a record with rua=, aggregate reports should begin arriving at the destination. If they don't, the most common issues are: cross-domain authorization missing (see above), the receiving mailbox rejecting the report message, the rua= address typo'd.
After both checks pass, the record is live. Note the publication date in the change record; the 30-60-day monitoring phase has begun.
Tools mentioned
- DMARC Record Generator — compose, validate, copy
- DMARC Validator — verify the published record's syntax
- DNS Lookup — confirm the record propagated correctly
- Email Header Analyzer — when individual messages fail, trace why
Next steps
If you're rolling DMARC out for a new client, start with the safe default — v=DMARC1; p=none; rua=mailto:<tenant>@<dmarc-platform> — published at the apex. Wait 30 days. Read the aggregate reports. Authenticate every legitimate sender you find. Once your DMARC pass rate is above 95%, move to p=quarantine; pct=25 and continue the phased rollout from there. The whole journey is documented step-by-step in the Phased DMARC rollout article; the generator turns each phase transition into a five-minute task.
Author: DMARC AI editorial team Last updated: June 2026