By Ahmet Karaca · Updated · Checked against RFC 9989 (DMARC, May 2026), RFC 9990 (DMARC aggregate reports), RFC 7489 (DMARC), Google's email sender guidelines, Yahoo Sender Hub best practices and Microsoft's Outlook.com requirements for high-volume senders on
What your DMARC record tells receivers
DMARC connects the two technical checks, SPF and DKIM, to the address people actually see in the From line. A message passes DMARC when at least one of them passes and is aligned with the From domain. Your record then tells receivers what to do with mail that fails, and where to send reports about it. What is DMARC? explains the whole decision step by step.
How a DMARC record is written
A DMARC record is a TXT record at _dmarc under the domain in your From address. It is a list of tag=value pairs separated by semicolons:
_dmarc.example.com. TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"RFC 9989, which replaced RFC 7489 in May 2026, defines these tags. Tags it doesn't know are ignored.
| Tag | Meaning | If left out |
|---|---|---|
| v | Version. Must be the first tag, and its value exactly DMARC1, or receivers ignore the whole record. | Required |
| p | Policy for mail that fails: none, quarantine or reject. | none if rua is valid, else no DMARC |
| sp | Policy for existing subdomains that have no record of their own. | Same as p |
| np | Policy for subdomains that don't exist in DNS. Part of the core standard since RFC 9989, which took it from RFC 9091. | Same as sp, then p |
| t | Testing. t=y asks receivers to apply one level less than the policy. New in RFC 9989. | n |
| rua | Where to send aggregate reports, as mailto: addresses separated by commas. | No reports |
| ruf | Where to send reports about single messages that failed. | No reports |
| adkim | DKIM alignment: r (relaxed) or s (strict). | r |
| aspf | SPF alignment: r (relaxed) or s (strict). | r |
| fo | Which failures trigger a ruf report. Ignored without ruf. | 0 |
| psd | y marks a public suffix, published by its operator; n marks the domain as the organizational domain for itself and its subdomains. | u |
RFC 9989 removed three tags. pct asked receivers to apply the policy to a share of failing mail; its job now belongs to t. rf, the format of failure reports, and ri, the interval between aggregate reports, are gone too. A record that still carries them keeps working, because receivers ignore tags they don't process.
What each part of the result means
- Policy. The
p=value with what it does: monitoring only, failing mail to spam, or failing mail blocked. - Enforcement. Whether receivers apply that policy today. Not enforced means
p=none. Testing meanst=yorpct=0, so receivers hold back. Enforced means the policy applies to all failing mail. A percentage appears only for an old record with apctbetween 0 and 100. - Reports go to. How many
ruaaddresses the record lists, and which ones. "Nobody" means you get no reports. - The record. The TXT record and the name it was found at. If that name is higher up the tree than the domain you entered, the domain has no record of its own and inherits that one. For an existing subdomain, receivers then take the policy from
sp=when the record has one, not fromp=. If receivers would ignore the record, because a tag is repeated, or its policy tag is missing or invalid and no validrua=URI is listed, we show its text without the policy boxes. - Findings. Problems first, then warnings, then notes, many with a link to the fix. With no findings you see "No problems found".
Common errors and how to fix them
These are the findings the checker can report, with the message you see in the result. Domains and values below are examples. Mistakes in the record itself come first:
- Problem: "example.com has no DMARC record. Anyone can send mail that claims to be from your domain." Publish a record at
_dmarc, starting withp=noneand a report address. The DMARC generator writes one. - Problem: "_dmarc.example.com has more than one DMARC record, so receivers ignore all of them." Keep one record and delete the rest. If a DNS host or email service created one for you, edit that record instead of adding another.
- Problem: "A DMARC record must start with v=DMARC1." The version tag must come first, say exactly
DMARC1and be separated from the next tag by a semicolon, so receivers ignore records likev=dmarc1; p=rejectorv=DMARC1 p=reject. We show this when the record at your_dmarcname looks like DMARC but its version tag is broken. - Problem: "'p reject' is not a valid tag=value pair." Separate tags with semicolons and give each one an equals sign.
- Problem: "The 'rua' tag appears more than once. DMARC uses the DKIM tag syntax (RFC 6376 §3.2), where a repeated tag makes the whole record invalid, so receivers following it ignore the record." Until you fix it, your domain has no DMARC policy at all, and the checker shows the record without its policy boxes. Merge the values into one tag; several addresses go in one
rua=, separated by commas. - Problem: "The record has no p= tag and no valid rua= URI, so receivers ignore it completely. Your domain has no DMARC protection or reporting." RFC 9989 tells receivers to skip DMARC for a record like this. Add
p=noneand arua=address to start monitoring, or a stronger policy once your reports are clean. - Warning: "The record has no p= tag. Because its rua= tag holds a valid report URI, receivers treat it as p=none: they may send reports, but nothing is protected." Add
p=noneexplicitly, then tighten it as your reports allow. - Problem: "p=rejected is not valid. Use none, quarantine or reject. Until you fix it, receivers treat the whole record as p=none." That is the message when
rua=holds a valid report URI. Without one it ends "With no valid rua= URI either, receivers ignore the whole record.", because RFC 9989 then tells receivers to skip DMARC. The same rule applies tosp=andnp=. - Problem: "pct=150 must be a whole number from 0 to 100." Better still, remove
pctand uset=ywhile you test. - Problem: "adkim=relaxed must be r (relaxed) or s (strict)." Use the single letter, or leave the tag out for relaxed.
- Problem: "'dmarc@example.com' in rua= is not a valid URI. Use a mailto: address." Each address needs the
mailto:prefix. Amailto:URI whose address is broken gets its own message: "'mailto:dmarc@' in rua= is not a valid mailto: address, so no reports reach it."
These are about the policy and reports rather than syntax:
- Warning: "p=none only monitors. Spoofed mail using your domain is still delivered." Fine while you collect reports; then move on. Moving from p=none to p=reject covers each stage.
- Warning: "No rua= address, so you receive no reports about who sends mail as your domain." Add
rua=mailto:with an address you read, or a monitoring service's address. - Warning: "Reports to reports.example.net may be dropped: it hasn't published authorization to receive reports for example.com." The receiving domain must publish
example.com._report._dmarc.reports.example.netwithv=DMARC1. Ask the service to add it, or check that you copied its address correctly. - Warning: "'https://reports.example.net/dmarc' in rua= is a valid URI, but most receivers only send reports to mailto: addresses, so it likely gets none." RFC 9989 allows any URI, but requires receivers that send reports to support only
mailto:and to ignore schemes they don't support. Use amailto:address. - Warning: "t=yes is not valid. Use t=y to test the policy or leave t out; receivers treat anything else as t=n and apply the policy." Write
t=yexactly. - Warning: "psd=y declares example.com a public suffix, like a TLD registry. Receivers then treat the name one label below as the organizational domain, which changes alignment for its subdomains. Remove it unless you operate a public suffix." Delete
psd=y. If you want a boundary inside your own DNS,psd=nis the value for that; the psd tag section explains both. - Warning: "psd=yes is not valid. Use y, n or u, or leave it out. Receivers fall back to psd=u." Most domains leave
psdout entirely. - Warning: "fo=0:1 is not valid. Use 0, 1, d or s, joined with colons (for example fo=1:d), and not 0 and 1 together. Receivers fall back to fo=0." Separate values with colons, not commas. The fo tag section lists what each value asks for.
- Warning: "'policy' is not a DMARC tag and will be ignored." Usually a typo for a real tag; compare it with the table above.
- Note: "fo= only shapes failure reports, and the record has no ruf= address, so receivers ignore it. You can delete it." Keep
foonly next to arufaddress you actually read. - Note: "Your policy is in testing mode: receivers are expected to treat it one level lower, so p=reject acts like quarantine and p=quarantine like none. Reports still arrive and show what enforcement would change." Remove
t=yandpct=0when the reports show every legitimate sender passing. - Note: "Receivers that still follow RFC 7489 don't know t=y and apply the full policy. Add pct=0 next to t=y so they hold back too." Publish
t=y; pct=0together during testing. - Note: "pct=50 asks receivers to apply the policy to part of your failing mail. RFC 9989 removed pct: receivers following it ignore the tag and apply the full policy, while older RFC 7489 receivers may still apply part. Use t=y while you test, then the full policy." Replace the partial
pctwitht=y, or remove it to enforce. - Note: "RFC 9989 removed the ri= tag, so it has no effect. You can delete it." The same note covers
rf=. - Note: "Subdomains use a weaker policy (sp=none) than the main domain." Intended while subdomains are still being set up; raise
spwhen they are ready. - Note: "mail.example.com has no DMARC record of its own, so receivers use the one on example.com." Nothing to fix unless the subdomain needs a different policy; then publish a record at its own
_dmarc.
And these mean DNS didn't give us a full answer:
- Problem: "We couldn't query DNS for _dmarc.example.com. Try again in a minute." DNS failed before we found any record, so we can't say what applies. We don't keep this result.
- Warning: "We couldn't finish the DMARC lookup at _dmarc.com, so the policy shown may not be the final one. Try again in a minute." We found a record, but a later query failed; a record at a shorter name could still change the outcome.
- Warning: "We couldn't verify whether reports.example.net may receive your reports. Try again in a minute." The authorization lookup timed out or failed; it says nothing about the address itself.
How we check
We find the record the way RFC 9989 tells receivers to. First we ask for the TXT records at _dmarc under the domain you entered and keep only those whose first tag is v=DMARC1. If exactly one is left, that is the record. If more than one is left, receivers ignore them all, and so do we.
If the domain has no usable record of its own, we walk up the DNS tree: we remove the leftmost label and ask again, one level at a time, until a record with a psd= tag stops the walk or no labels remain. Finding a record doesn't end the walk, because a record at a shorter name can still change which domain counts as the organizational one. A walk makes at most 8 queries; for a name with more labels than that, the second query jumps straight to the last 7 labels. For news.mail.example.com it looks like this:
_dmarc.news.mail.example.com no record
_dmarc.mail.example.com no record
_dmarc.example.com v=DMARC1; p=reject; sp=quarantine
_dmarc.com no record
→ example.com is the organizational domain; its sp=quarantine appliesFrom the records found, we pick the organizational domain using the rules in section 4.10.2 of the RFC: a psd=n record marks it, a psd=y record means the name one label below it, and otherwise it is the record with the fewest labels. The record that applies is the organizational domain's; only if that has none do we fall back to a public suffix's psd=y record. When the record comes from a name higher up the tree, the result says the domain inherits it.
One case gets special care. If no valid record exists but one of the names we asked has a record whose version tag is broken, such as v=dmarc1, we show that record with the version error instead of reporting "no DMARC record", because its owner clearly meant to publish one.
Report addresses
For each rua address on a different organizational domain from the record, we check that the receiving domain agreed to take your reports, as RFC 9990 requires: a TXT record at yourdomain._report._dmarc.theirdomain starting with v=DMARC1. We work out both organizational domains with the same tree walk. We look at up to 3 distinct report domains per record; one that shares your organizational domain needs no authorization.
DNS problems and timing
If DNS fails before any record is found, we say so rather than guess. If it fails after a record was found, we show that record with a warning that a shorter name we couldn't reach might have changed the outcome. A check has 10 seconds to finish. This public checker reads DNS through a caching resolver, and we reuse a finished result for 5 minutes unless something failed, so a record you just changed can take a few minutes, or until its TTL runs out, to show here.
What it doesn't check
- Whether your mail passes DMARC. That depends on SPF and DKIM results for each message; the reports show it.
- Your SPF and DKIM records, which have their own checkers, or all three together in the domain health check.
- Whether report mailboxes accept mail; we only check that an outside domain authorized them.
The safe path to p=reject
Jumping straight to p=reject can block your own invoices or newsletters if a service you use isn't set up yet. Start with p=none and a report address, read the reports until every legitimate sender passes, then test quarantine with t=y before enforcing it, and do the same with reject. Moving from p=none to p=reject walks through every stage.
Need a record? Build one with the DMARC generator, or run a full domain health check to see SPF, DKIM and DMARC together.